162306a36Sopenharmony_ciNOTE: 262306a36Sopenharmony_ciThis is a version of Documentation/memory-barriers.txt translated into 362306a36Sopenharmony_ciSpanish by Carlos Bilbao <carlos.bilbao@amd.com>. If you find any 462306a36Sopenharmony_cidifference between this document and the original file or a problem with 562306a36Sopenharmony_cithe translation, please contact the maintainer of this file. Please also 662306a36Sopenharmony_cinote that the purpose of this file is to be easier to read for non English 762306a36Sopenharmony_ci(read: Spanish) speakers and is not intended as a fork. So if you have any 862306a36Sopenharmony_cicomments or updates for this file please update the original English file 962306a36Sopenharmony_cifirst. The English version is definitive, and readers should look there if 1062306a36Sopenharmony_cithey have any doubt. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci ====================================== 1362306a36Sopenharmony_ci BARRERAS DE MEMORIA EN EL KERNEL LINUX 1462306a36Sopenharmony_ci ====================================== 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ciDocumento original: David Howells <dhowells@redhat.com> 1762306a36Sopenharmony_ci Paul E. McKenney <paulmck@linux.ibm.com> 1862306a36Sopenharmony_ci Will Deacon <will.deacon@arm.com> 1962306a36Sopenharmony_ci Peter Zijlstra <peterz@infradead.org> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciTraducido por: Carlos Bilbao <carlos.bilbao@amd.com> 2262306a36Sopenharmony_ciNota: Si tiene alguna duda sobre la exactitud del contenido de esta 2362306a36Sopenharmony_citraducción, la única referencia válida es la documentación oficial en 2462306a36Sopenharmony_ciinglés. 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci=========== 2762306a36Sopenharmony_ciADVERTENCIA 2862306a36Sopenharmony_ci=========== 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciEste documento no es una especificación; es intencionalmente (por motivos 3162306a36Sopenharmony_cide brevedad) y sin querer (por ser humanos) incompleta. Este documento 3262306a36Sopenharmony_cipretende ser una guía para usar las diversas barreras de memoria 3362306a36Sopenharmony_ciproporcionadas por Linux, pero ante cualquier duda (y hay muchas) por favor 3462306a36Sopenharmony_cipregunte. Algunas dudas pueden ser resueltas refiriéndose al modelo de 3562306a36Sopenharmony_ciconsistencia de memoria formal y documentación en tools/memory-model/. Sin 3662306a36Sopenharmony_ciembargo, incluso este modelo debe ser visto como la opinión colectiva de 3762306a36Sopenharmony_cisus maintainers en lugar de que como un oráculo infalible. 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciDe nuevo, este documento no es una especificación de lo que Linux espera 4062306a36Sopenharmony_cidel hardware. 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciEl propósito de este documento es doble: 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci (1) especificar la funcionalidad mínima en la que se puede confiar para 4562306a36Sopenharmony_ci cualquier barrera en concreto, y 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci (2) proporcionar una guía sobre cómo utilizar las barreras disponibles. 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciTenga en cuenta que una arquitectura puede proporcionar más que el 5062306a36Sopenharmony_cirequisito mínimo para cualquier barrera en particular, pero si la 5162306a36Sopenharmony_ciarquitectura proporciona menos de eso, dicha arquitectura es incorrecta. 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciTenga en cuenta también que es posible que una barrera no valga (sea no-op) 5462306a36Sopenharmony_cipara alguna arquitectura porque por la forma en que funcione dicha 5562306a36Sopenharmony_ciarquitectura, la barrera explícita resulte innecesaria en ese caso. 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci========== 5862306a36Sopenharmony_ciCONTENIDOS 5962306a36Sopenharmony_ci========== 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci (*) Modelo abstracto de acceso a memoria. 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci - Operaciones del dispositivo. 6462306a36Sopenharmony_ci - Garantías. 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci (*) ¿Qué son las barreras de memoria? 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci - Variedades de barrera de memoria. 6962306a36Sopenharmony_ci - ¿Qué no se puede asumir sobre las barreras de memoria? 7062306a36Sopenharmony_ci - Barreras de dirección-dependencia (históricas). 7162306a36Sopenharmony_ci - Dependencias de control. 7262306a36Sopenharmony_ci - Emparejamiento de barreras smp. 7362306a36Sopenharmony_ci - Ejemplos de secuencias de barrera de memoria. 7462306a36Sopenharmony_ci - Barreras de memoria de lectura frente a especulación de carga. 7562306a36Sopenharmony_ci - Atomicidad multicopia. 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci (*) Barreras explícitas del kernel. 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci - Barrera del compilador. 8062306a36Sopenharmony_ci - Barreras de memoria de la CPU. 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci (*) Barreras de memoria implícitas del kernel. 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci - Funciones de adquisición de cerrojo. 8562306a36Sopenharmony_ci - Funciones de desactivación de interrupciones. 8662306a36Sopenharmony_ci - Funciones de dormir y despertar. 8762306a36Sopenharmony_ci - Funciones varias. 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci (*) Efectos de barrera adquiriendo intra-CPU. 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci - Adquisición vs accesos a memoria. 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci (*) ¿Dónde se necesitan barreras de memoria? 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci - Interacción entre procesadores. 9662306a36Sopenharmony_ci - Operaciones atómicas. 9762306a36Sopenharmony_ci - Acceso a dispositivos. 9862306a36Sopenharmony_ci - Interrupciones. 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci (*) Efectos de barrera de E/S del kernel. 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci (*) Modelo de orden mínimo de ejecución asumido. 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci (*) Efectos de la memoria caché de la CPU. 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci - Coherencia de caché. 10762306a36Sopenharmony_ci - Coherencia de caché frente a DMA. 10862306a36Sopenharmony_ci - Coherencia de caché frente a MMIO. 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci (*) Cosas que hacen las CPU. 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci - Y luego está el Alfa. 11362306a36Sopenharmony_ci - Guests de máquinas virtuales. 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci (*) Ejemplos de usos. 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci - Buffers circulares. 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci (*) Referencias. 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci==================================== 12362306a36Sopenharmony_ciMODELO ABSTRACTO DE ACCESO A MEMORIA 12462306a36Sopenharmony_ci==================================== 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciConsidere el siguiente modelo abstracto del sistema: 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci : : 12962306a36Sopenharmony_ci : : 13062306a36Sopenharmony_ci : : 13162306a36Sopenharmony_ci +-------+ : +--------+ : +-------+ 13262306a36Sopenharmony_ci | | : | | : | | 13362306a36Sopenharmony_ci | | : | | : | | 13462306a36Sopenharmony_ci | CPU 1 |<----->| Memoria|<----->| CPU 2 | 13562306a36Sopenharmony_ci | | : | | : | | 13662306a36Sopenharmony_ci | | : | | : | | 13762306a36Sopenharmony_ci +-------+ : +--------+ : +-------+ 13862306a36Sopenharmony_ci ^ : ^ : ^ 13962306a36Sopenharmony_ci | : | : | 14062306a36Sopenharmony_ci | : | : | 14162306a36Sopenharmony_ci | : v : | 14262306a36Sopenharmony_ci | : +--------+ : | 14362306a36Sopenharmony_ci | : | | : | 14462306a36Sopenharmony_ci | : | Disposi| : | 14562306a36Sopenharmony_ci +---------->| tivo |<----------+ 14662306a36Sopenharmony_ci : | | : 14762306a36Sopenharmony_ci : | | : 14862306a36Sopenharmony_ci : +--------+ : 14962306a36Sopenharmony_ci : : 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciCada CPU ejecuta un programa que genera operaciones de acceso a la memoria. 15262306a36Sopenharmony_ciEn la CPU abstracta, el orden de las operaciones de memoria es muy 15362306a36Sopenharmony_cirelajado, y una CPU en realidad puede realizar las operaciones de memoria 15462306a36Sopenharmony_cien el orden que desee, siempre que la causalidad del programa parezca 15562306a36Sopenharmony_cimantenerse. De manera similar, el compilador también puede organizar las 15662306a36Sopenharmony_ciinstrucciones que emite en el orden que quiera, siempre que no afecte al 15762306a36Sopenharmony_cifuncionamiento aparente del programa. 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciEntonces, en el diagrama anterior, los efectos de las operaciones de 16062306a36Sopenharmony_cimemoria realizadas por un CPU son percibidos por el resto del sistema a 16162306a36Sopenharmony_cimedida que las operaciones cruzan la interfaz entre la CPU y el resto del 16262306a36Sopenharmony_cisistema (las líneas discontinuas a puntos). 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciPor ejemplo, considere la siguiente secuencia de eventos: 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci CPU 1 CPU 2 16762306a36Sopenharmony_ci =============== =============== 16862306a36Sopenharmony_ci { A == 1; B == 2 } 16962306a36Sopenharmony_ci A = 3; x = B; 17062306a36Sopenharmony_ci B = 4; y = A; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciEl conjunto de accesos visto por el sistema de memoria en el medio se puede 17362306a36Sopenharmony_ciorganizar en 24 combinaciones diferentes (donde LOAD es cargar y STORE es 17462306a36Sopenharmony_ciguardar): 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciSTORE A=3, STORE B=4, y=LOAD A->3, x=LOAD B->4 17762306a36Sopenharmony_ciSTORE A=3, STORE B=4, x=LOAD B->4, y=LOAD A->3 17862306a36Sopenharmony_ciSTORE A=3, y=LOAD A->3, STORE B=4, x=LOAD B->4 17962306a36Sopenharmony_ciSTORE A=3, y=LOAD A->3, x=LOAD B->2, STORE B=4 18062306a36Sopenharmony_ciSTORE A=3, x=LOAD B->2, STORE B=4, y=LOAD A->3 18162306a36Sopenharmony_ciSTORE A=3, x=LOAD B->2, y=LOAD A->3, STORE B=4 18262306a36Sopenharmony_ciSTORE B=4, STORE A=3, y=LOAD A->3, x=LOAD B->4 18362306a36Sopenharmony_ciSTORE B=4, ... 18462306a36Sopenharmony_ci... 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciy por lo tanto puede resultar en cuatro combinaciones diferentes de 18762306a36Sopenharmony_civalores: 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cix == 2, y == 1 19062306a36Sopenharmony_cix == 2, y == 3 19162306a36Sopenharmony_cix == 4, y == 1 19262306a36Sopenharmony_cix == 4, y == 3 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciAdemás, los stores asignados por una CPU al sistema de memoria pueden no 19562306a36Sopenharmony_ciser percibidos por los loads realizados por otra CPU en el mismo orden en 19662306a36Sopenharmony_cique fueron realizados. 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciComo otro ejemplo, considere esta secuencia de eventos: 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci CPU 1 CPU 2 20162306a36Sopenharmony_ci =============== =============== 20262306a36Sopenharmony_ci { A == 1, B == 2, C == 3, P == &A, Q == &C } 20362306a36Sopenharmony_ci B = 4; Q = P; 20462306a36Sopenharmony_ci P = &B; D = *Q; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciAquí hay una dependencia obvia de la dirección, ya que el valor cargado en 20762306a36Sopenharmony_ciD depende en la dirección recuperada de P por la CPU 2. Al final de la 20862306a36Sopenharmony_cisecuencia, cualquiera de los siguientes resultados son posibles: 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci (Q == &A) y (D == 1) 21162306a36Sopenharmony_ci (Q == &B) y (D == 2) 21262306a36Sopenharmony_ci (Q == &B) y (D == 4) 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciTenga en cuenta que la CPU 2 nunca intentará cargar C en D porque la CPU 21562306a36Sopenharmony_cicargará P en Q antes de emitir la carga de *Q. 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciOPERACIONES DEL DISPOSITIVO 21862306a36Sopenharmony_ci--------------------------- 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciAlgunos dispositivos presentan sus interfaces de control como colecciones 22162306a36Sopenharmony_cide ubicaciones de memoria, pero el orden en que se accede a los registros 22262306a36Sopenharmony_cide control es muy importante. Por ejemplo, imagine una tarjeta ethernet con 22362306a36Sopenharmony_ciun conjunto de registros a los que se accede a través de un registro de 22462306a36Sopenharmony_cipuerto de dirección (A) y un registro de datos del puerto (D). Para leer el 22562306a36Sopenharmony_ciregistro interno 5, el siguiente código podría entonces ser usado: 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci *A = 5; 22862306a36Sopenharmony_ci x = *D; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cipero esto podría aparecer como cualquiera de las siguientes dos secuencias: 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci STORE *A = 5, x = LOAD *D 23362306a36Sopenharmony_ci x = LOAD *D, STORE *A = 5 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ciel segundo de las cuales casi con certeza resultará en mal funcionamiento, 23662306a36Sopenharmony_ciya que se estableció la dirección _después_ de intentar leer el registro. 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ciGARANTÍAS 24062306a36Sopenharmony_ci--------- 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciHay algunas garantías mínimas que se pueden esperar de una CPU: 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci (*) En cualquier CPU dada, los accesos a la memoria dependiente se 24562306a36Sopenharmony_ci emitirán en orden, con respeto a sí mismo. Esto significa que para: 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci Q = READ_ONCE(P); D = READ_ONCE(*Q); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci donde READ_ONCE() es LEER_UNA_VEZ(), la CPU emitirá las siguientes 25062306a36Sopenharmony_ci operaciones de memoria: 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci Q = LOAD P, D = LOAD *Q 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci y siempre en ese orden. Sin embargo, en DEC Alpha, READ_ONCE() también 25562306a36Sopenharmony_ci emite una instrucción de barrera de memoria, de modo que una CPU DEC 25662306a36Sopenharmony_ci Alpha, sin embargo emite las siguientes operaciones de memoria: 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci Q = LOAD P, MEMORY_BARRIER, D = LOAD *Q, MEMORY_BARRIER 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci Ya sea en DEC Alpha o no, READ_ONCE() también evita que el compilador 26162306a36Sopenharmony_ci haga cosas inapropiadas. 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci (*) Los loads y stores superpuestos dentro de una CPU en particular 26462306a36Sopenharmony_ci parecerán ser ordenados dentro de esa CPU. Esto significa que para: 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci a = READ_ONCE(*X); WRITE_ONCE(*X, b); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci donde WRITE_ONCE() es ESCRIBIR_UNA_VEZ(), la CPU solo emitirá la 26962306a36Sopenharmony_ci siguiente secuencia de operaciones de memoria: 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci a = LOAD *X, STORE *X = b 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci Y para: 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci WRITE_ONCE(*X, c); d = READ_ONCE(*X); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci la CPU solo emitirá: 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci STORE *X = c, d = LOAD *X 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci (Los loads y stores se superponen si están destinados a piezas 28262306a36Sopenharmony_ci superpuestas de memoria). 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ciY hay una serie de cosas que _deben_ o _no_ deben asumirse: 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci (*) _No_debe_ asumirse que el compilador hará lo que usted quiera 28762306a36Sopenharmony_ci con referencias de memoria que no están protegidas por READ_ONCE() y 28862306a36Sopenharmony_ci WRITE ONCE(). Sin ellos, el compilador tiene derecho a hacer todo tipo 28962306a36Sopenharmony_ci de transformaciones "creativas", que se tratan en la sección BARRERA 29062306a36Sopenharmony_ci DEL COMPILADOR. 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci (*) _No_debe_ suponerse que se emitirán loads y stores independientes 29362306a36Sopenharmony_ci en el orden dado. Esto significa que para: 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci X = *A; Y = *B; *D = Z; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci podemos obtener cualquiera de las siguientes secuencias: 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci X = LOAD *A, Y = LOAD *B, STORE *D = Z 30062306a36Sopenharmony_ci X = LOAD *A, STORE *D = Z, Y = LOAD *B 30162306a36Sopenharmony_ci Y = LOAD *B, X = LOAD *A, STORE *D = Z 30262306a36Sopenharmony_ci Y = LOAD *B, STORE *D = Z, X = LOAD *A 30362306a36Sopenharmony_ci STORE *D = Z, X = LOAD *A, Y = LOAD *B 30462306a36Sopenharmony_ci STORE *D = Z, Y = LOAD *B, X = LOAD *A 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci (*) Se _debe_ suponer que los accesos de memoria superpuestos pueden 30762306a36Sopenharmony_ci fusionarse o ser descartados. Esto significa que para: 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci X = *A; Y = *(A + 4); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci podemos obtener cualquiera de las siguientes secuencias: 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciX = LOAD *A; Y = LOAD *(A + 4); 31462306a36Sopenharmony_ciY = LOAD *(A + 4); X = LOAD *A; 31562306a36Sopenharmony_ci{X, Y} = LOAD {*A, *(A + 4) }; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci Y para: 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci*A = X; *(A + 4) = Y; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci podemos obtener cualquiera de: 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciSTORE *A = X; STORE *(A + 4) = Y; 32462306a36Sopenharmony_ciSTORE *(A + 4) = Y; STORE *A = X; 32562306a36Sopenharmony_ciSTORE {*A, *(A + 4) } = {X, Y}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciY hay anti-garantías: 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci(*) Estas garantías no se aplican a los campos de bits, porque los 33062306a36Sopenharmony_ci compiladores a menudo generan código para modificarlos usando 33162306a36Sopenharmony_ci secuencias de lectura-modificación-escritura no atómica. No intente 33262306a36Sopenharmony_ci utilizar campos de bits para sincronizar algoritmos paralelos. 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci(*) Incluso en los casos en que los campos de bits están protegidos por 33562306a36Sopenharmony_ci cerrojos (o "cerrojos", o "locks"), todos los componentes en un campo 33662306a36Sopenharmony_ci de bits dado deben estar protegidos por un candado. Si dos campos en un 33762306a36Sopenharmony_ci campo de bits dado están protegidos por diferentes locks, las 33862306a36Sopenharmony_ci secuencias de lectura-modificación-escritura no atómicas del lock 33962306a36Sopenharmony_ci pueden causar una actualización a una campo para corromper el valor de 34062306a36Sopenharmony_ci un campo adyacente. 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci(*) Estas garantías se aplican solo a escalares correctamente alineados y 34362306a36Sopenharmony_ci dimensionados. De "tamaño adecuado" significa actualmente variables que 34462306a36Sopenharmony_ci son del mismo tamaño que "char", "short", "int" y "long". 34562306a36Sopenharmony_ci "Adecuadamente alineado" significa la alineación natural, por lo tanto, 34662306a36Sopenharmony_ci no hay restricciones para "char", alineación de dos bytes para "short", 34762306a36Sopenharmony_ci alineación de cuatro bytes para "int", y alineación de cuatro u ocho 34862306a36Sopenharmony_ci bytes para "long", en sistemas de 32 y 64 bits, respectivamente. Tenga 34962306a36Sopenharmony_ci en cuenta que estos garantías se introdujeron en el estándar C11, así 35062306a36Sopenharmony_ci que tenga cuidado cuando utilice compiladores anteriores a C11 (por 35162306a36Sopenharmony_ci ejemplo, gcc 4.6). La parte de la norma que contiene esta garantía es 35262306a36Sopenharmony_ci la Sección 3.14, que define "ubicación de memoria" de la siguiente 35362306a36Sopenharmony_ci manera: 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci ubicación de memoria 35662306a36Sopenharmony_ci ya sea un objeto de tipo escalar, o una secuencia máxima 35762306a36Sopenharmony_ci de campos de bits adyacentes, todos con ancho distinto de cero 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci NOTE 1: Dos hilos de ejecución pueden actualizar y acceder 36062306a36Sopenharmony_ci ubicaciones de memoria separadas sin interferir entre 36162306a36Sopenharmony_ci ellos. 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci NOTE 2: Un campo de bits y un miembro adyacente que no es un campo de 36462306a36Sopenharmony_ci bits están en ubicaciones de memoria separadas. Lo mismo sucede con 36562306a36Sopenharmony_ci dos campos de bits, si uno se declara dentro de un declaración de 36662306a36Sopenharmony_ci estructura anidada y el otro no, o si las dos están separados por una 36762306a36Sopenharmony_ci declaración de campo de bits de longitud cero, o si están separados por 36862306a36Sopenharmony_ci un miembro no declarado como campo de bits. No es seguro actualizar 36962306a36Sopenharmony_ci simultáneamente dos campos de bits en la misma estructura si entre 37062306a36Sopenharmony_ci todos los miembros declarados también hay campos de bits, sin importar 37162306a36Sopenharmony_ci cuál resulta ser el tamaño de estos campos de bits intermedios. 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci================================== 37562306a36Sopenharmony_ci¿QUÉ SON LAS BARRERAS DE MEMORIA? 37662306a36Sopenharmony_ci================================== 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ciComo se puede leer arriba, las operaciones independientes de memoria se 37962306a36Sopenharmony_cirealizan de manera efectiva en orden aleatorio, pero esto puede ser un 38062306a36Sopenharmony_ciproblema para la interacción CPU-CPU y para la E/S ("I/O"). Lo que se 38162306a36Sopenharmony_cirequiere es alguna forma de intervenir para instruir al compilador y al 38262306a36Sopenharmony_ciCPU para restringir el orden. 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ciLas barreras de memoria son este tipo de intervenciones. Imponen una 38562306a36Sopenharmony_cipercepción de orden parcial, sobre las operaciones de memoria a ambos lados 38662306a36Sopenharmony_cide la barrera. 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ciTal cumplimiento es importante porque las CPUs y otros dispositivos en un 38962306a36Sopenharmony_cisistema pueden usar una variedad de trucos para mejorar el rendimiento, 39062306a36Sopenharmony_ciincluido el reordenamiento, diferimiento y combinación de operaciones de 39162306a36Sopenharmony_cimemoria; cargas especulativas; predicción de "branches" especulativos y 39262306a36Sopenharmony_civarios tipos de almacenamiento en caché. Las barreras de memoria se 39362306a36Sopenharmony_ciutilizan para anular o suprimir estos trucos, permitiendo que el código 39462306a36Sopenharmony_cicontrole sensatamente la interacción de múltiples CPU y/o dispositivos. 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ciVARIEDADES DE BARRERA DE MEMORIA 39862306a36Sopenharmony_ci--------------------------------- 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ciLas barreras de memoria vienen en cuatro variedades básicas: 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci (1) Barreras de memoria al escribir o almacenar (Write or store memory 40362306a36Sopenharmony_ci barriers). 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci Una barrera de memoria de escritura garantiza que todas las 40662306a36Sopenharmony_ci operaciones de STORE especificadas antes de que la barrera aparezca 40762306a36Sopenharmony_ci suceden antes de todas las operaciones STORE especificadas después 40862306a36Sopenharmony_ci de la barrera, con respecto a los otros componentes del sistema. 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci Una barrera de escritura es un orden parcial solo en los stores; No 41162306a36Sopenharmony_ci es requerido que tenga ningún efecto sobre los loads. 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci Se puede considerar que una CPU envía una secuencia de operaciones de 41462306a36Sopenharmony_ci store al sistema de memoria a medida que pasa el tiempo. Todos los 41562306a36Sopenharmony_ci stores _antes_ de una barrera de escritura ocurrirán _antes_ de todos 41662306a36Sopenharmony_ci los stores después de la barrera de escritura. 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci [!] Tenga en cuenta que las barreras de escritura normalmente deben 41962306a36Sopenharmony_ci combinarse con read o barreras de address-dependency barriers 42062306a36Sopenharmony_ci (dependencia de dirección); consulte la subsección 42162306a36Sopenharmony_ci "Emparejamiento de barreras smp". 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci (2) Barrera de dependencia de dirección (histórico). 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci Una barrera de dependencia de dirección es una forma más débil de 42762306a36Sopenharmony_ci barrera de lectura. En el caso de que se realicen dos loads de manera 42862306a36Sopenharmony_ci que la segunda dependa del resultado de la primera (por ejemplo: el 42962306a36Sopenharmony_ci primer load recupera la dirección a la que se dirigirá el segundo 43062306a36Sopenharmony_ci load), una barrera de dependencia de dirección sería necesaria para 43162306a36Sopenharmony_ci asegurarse de que el objetivo de la segunda carga esté actualizado 43262306a36Sopenharmony_ci después de acceder a la dirección obtenida por la primera carga. 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci Una barrera de dependencia de direcciones es una ordenación parcial en 43562306a36Sopenharmony_ci laods de direcciones interdependientes; no se requiere que tenga 43662306a36Sopenharmony_ci ningún efecto en los stores, ya sean cargas de memoria o cargas 43762306a36Sopenharmony_ci de memoria superpuestas. 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci Como se mencionó en (1), las otras CPU en el sistema pueden verse como 44062306a36Sopenharmony_ci secuencias de stores en el sistema de memoria que la considerada CPU 44162306a36Sopenharmony_ci puede percibir. Una barrera de dependencia de dirección emitida por 44262306a36Sopenharmony_ci la CPU en cuestión garantiza que para cualquier carga que la preceda, 44362306a36Sopenharmony_ci si esa carga toca alguna secuencia de stores de otra CPU, entonces 44462306a36Sopenharmony_ci en el momento en que la barrera se complete, los efectos de todos los 44562306a36Sopenharmony_ci stores antes del cambio del load serán perceptibles por cualquier 44662306a36Sopenharmony_ci carga emitida después la barrera de la dependencia de la dirección. 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci Consulte la subsección "Ejemplos de secuencias de barrera de memoria" 44962306a36Sopenharmony_ci para ver los diagramas mostrando las restricciones de orden. 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci [!] Tenga en cuenta que la primera carga realmente tiene que tener una 45262306a36Sopenharmony_ci dependencia de _dirección_ y no es una dependencia de control. Si la 45362306a36Sopenharmony_ci dirección para la segunda carga depende de la primera carga, pero la 45462306a36Sopenharmony_ci dependencia es a través de un condicional en lugar de -en realidad- 45562306a36Sopenharmony_ci cargando la dirección en sí, entonces es una dependencia de _control_ 45662306a36Sopenharmony_ci y se requiere una barrera de lectura completa o superior. Consulte la 45762306a36Sopenharmony_ci subsección "Dependencias de control" para más información. 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci [!] Tenga en cuenta que las barreras de dependencia de dirección 46062306a36Sopenharmony_ci normalmente deben combinarse con barreras de escritura; consulte la 46162306a36Sopenharmony_ci subsección "Emparejamiento de barreras smp". 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci [!] Desde el kernel v5.9, se eliminó la API del kernel para barreras 46462306a36Sopenharmony_ci de memoria de direcciones explícitas. Hoy en día, las APIs para marcar 46562306a36Sopenharmony_ci cargas de variables compartidas, como READ_ONCE() y rcu_dereference(), 46662306a36Sopenharmony_ci proporcionan barreras de dependencia de dirección implícitas. 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci (3) Barreras de memoria al leer o cargar (Read or load memory 46962306a36Sopenharmony_ci barriers). 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci Una barrera de lectura es una barrera de dependencia de direcciones, 47262306a36Sopenharmony_ci más una garantía de que todas las operaciones de LOAD especificadas 47362306a36Sopenharmony_ci antes de la barrera parecerán ocurrir antes de todas las operaciones 47462306a36Sopenharmony_ci de LOAD especificadas después de la barrera con respecto a los demás 47562306a36Sopenharmony_ci componentes del sistema. 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci Una barrera de lectura es un orden parcial solo en cargas; no es 47862306a36Sopenharmony_ci necesario que tenga ningún efecto en los stores. 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci Las barreras de memoria de lectura implican barreras de dependencia de 48162306a36Sopenharmony_ci direcciones, y por tanto puede sustituirlas por estas. 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci [!] Tenga en mente que las barreras de lectura normalmente deben 48462306a36Sopenharmony_ci combinarse con barreras de escritura; consulte la subsección 48562306a36Sopenharmony_ci "Emparejamiento de barreras smp". 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci (4) Barreras de memoria generales 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci Una barrera de memoria general proporciona la garantía de que todas 49062306a36Sopenharmony_ci las operaciones LOAD y STORE especificadas antes de que la barrera 49162306a36Sopenharmony_ci aparezca suceden antes de que todas las operaciones LOAD y STORE 49262306a36Sopenharmony_ci especificadas después de la barrera con respecto a los demás 49362306a36Sopenharmony_ci componentes del sistema. 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci Una barrera de memoria general es un orden parcial tanto en 49662306a36Sopenharmony_ci operaciones de carga como de almacenamiento. 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci Las barreras de memoria generales implican barreras de memoria tanto 49962306a36Sopenharmony_ci de lectura como de escritura, de modo que pueden sustituir a 50062306a36Sopenharmony_ci cualquiera. 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ciY un par de variedades implícitas: 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci (5) ACQUIRE (de adquisición). 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci Esto actúa como una barrera permeable unidireccional. Garantiza que 50762306a36Sopenharmony_ci toda las operaciones de memoria después de la operación ACQUIRE 50862306a36Sopenharmony_ci parezcan suceder después de la ACQUIRE con respecto a los demás 50962306a36Sopenharmony_ci componentes del sistema. Las operaciones ACQUIRE incluyen operaciones 51062306a36Sopenharmony_ci LOCK y smp_load_acquire(), y operaciones smp_cond_load_acquire(). 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci Las operaciones de memoria que ocurren antes de una operación ACQUIRE 51362306a36Sopenharmony_ci pueden parecer suceder después de que se complete. 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci Una operación ACQUIRE casi siempre debe estar emparejada con una 51662306a36Sopenharmony_ci operación RELEASE (de liberación). 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci (6) Operaciones RELEASE (de liberación). 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci Esto también actúa como una barrera permeable unidireccional. 52262306a36Sopenharmony_ci Garantiza que todas las operaciones de memoria antes de la operación 52362306a36Sopenharmony_ci RELEASE parecerán ocurrir antes de la operación RELEASE con respecto a 52462306a36Sopenharmony_ci los demás componentes del sistema. Las operaciones de RELEASE incluyen 52562306a36Sopenharmony_ci operaciones de UNLOCK y operaciones smp_store_release(). 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci Las operaciones de memoria que ocurren después de una operación 52862306a36Sopenharmony_ci RELEASE pueden parecer suceder antes de que se complete. 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci El uso de las operaciones ACQUIRE y RELEASE generalmente excluye la 53162306a36Sopenharmony_ci necesidad de otros tipos de barrera de memoria. Además, un par 53262306a36Sopenharmony_ci RELEASE+ACQUIRE NO garantiza actuar como una barrera de memoria 53362306a36Sopenharmony_ci completa. Sin embargo, después de un ACQUIRE de una variable dada, 53462306a36Sopenharmony_ci todos los accesos a la memoria que preceden a cualquier anterior 53562306a36Sopenharmony_ci RELEASE en esa misma variable están garantizados como visibles. En 53662306a36Sopenharmony_ci otras palabras, dentro de la sección crítica de una variable dada, 53762306a36Sopenharmony_ci todos los accesos de todas las secciones críticas anteriores para esa 53862306a36Sopenharmony_ci variable habrán terminado de forma garantizada. 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci Esto significa que ACQUIRE actúa como una operación mínima de 54162306a36Sopenharmony_ci "adquisición" y RELEASE actúa como una operación mínima de 54262306a36Sopenharmony_ci "liberación". 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ciUn subconjunto de las operaciones atómicas descritas en atomic_t.txt 54562306a36Sopenharmony_cicontiene variantes de ACQUIRE y RELEASE, además de definiciones 54662306a36Sopenharmony_cicompletamente ordenadas o relajadas (sin barrera semántica). Para 54762306a36Sopenharmony_cicomposiciones atómicas que realizan tanto un load como store, la semántica 54862306a36Sopenharmony_ciACQUIRE se aplica solo a la carga y la semántica RELEASE se aplica sólo a 54962306a36Sopenharmony_cila parte de la operación del store. 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ciLas barreras de memoria solo son necesarias cuando existe la posibilidad de 55262306a36Sopenharmony_ciinteracción entre dos CPU o entre una CPU y un dispositivo. Si se puede 55362306a36Sopenharmony_cigarantizar que no habrá tal interacción en ninguna pieza de código en 55462306a36Sopenharmony_ciparticular, entonces las barreras de memoria son innecesarias en ese 55562306a36Sopenharmony_cifragmento de código. 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ciTenga en cuenta que estas son las garantías _mínimas_. Diferentes 55862306a36Sopenharmony_ciarquitecturas pueden proporcionar garantías más sustanciales, pero no se 55962306a36Sopenharmony_cipuede confiar en estas fuera de esa arquitectura en específico. 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci¿QUÉ NO SE PUEDE ASUMIR SOBRE LAS BARRERAS DE LA MEMORIA? 56362306a36Sopenharmony_ci--------------------------------------------------------- 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ciHay ciertas cosas que las barreras de memoria del kernel Linux no 56662306a36Sopenharmony_cigarantizan: 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci (*) No hay garantía de que ninguno de los accesos a la memoria 56962306a36Sopenharmony_ci especificados antes de una barrera de memoria estará _completo_ al 57062306a36Sopenharmony_ci completarse una instrucción de barrera de memoria; se puede considerar 57162306a36Sopenharmony_ci que la barrera dibuja una línea en la cola de acceso del CPU que no 57262306a36Sopenharmony_ci pueden cruzar los accesos del tipo correspondiente. 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci (*) No hay garantía de que la emisión de una barrera de memoria en una CPU 57562306a36Sopenharmony_ci tenga cualquier efecto directo en otra CPU o cualquier otro hardware 57662306a36Sopenharmony_ci en el sistema. El efecto indirecto será el orden en que la segunda CPU 57762306a36Sopenharmony_ci ve los efectos de los primeros accesos que ocurren de la CPU, pero lea 57862306a36Sopenharmony_ci el siguiente argumento: 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci (*) No hay garantía de que una CPU vea el orden correcto de los efectos 58162306a36Sopenharmony_ci de los accesos de una segunda CPU, incluso _si_ la segunda CPU usa una 58262306a36Sopenharmony_ci barrera de memoria, a menos que la primera CPU _también_ use una 58362306a36Sopenharmony_ci barrera de memoria coincidente (vea el subapartado "Emparejamiento de 58462306a36Sopenharmony_ci barrera SMP"). 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci (*) No hay garantía de que alguna pieza intermedia fuera del hardware[*] 58762306a36Sopenharmony_ci del CPU no reordenará los accesos a la memoria. Los mecanismos de 58862306a36Sopenharmony_ci coherencia de caché del CPU deben propagar los efectos indirectos de 58962306a36Sopenharmony_ci una barrera de memoria entre las CPU, pero es posible que no lo hagan 59062306a36Sopenharmony_ci en orden. 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci [*] Para obtener información sobre bus mastering DMA y coherencia, lea: 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci Documentation/driver-api/pci/pci.rst 59562306a36Sopenharmony_ci Documentation/core-api/dma-api-howto.rst 59662306a36Sopenharmony_ci Documentation/core-api/dma-api.rst 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ciBARRERA DE DEPENDENCIA DE DIRECCIÓN (HISTÓRICO) 60062306a36Sopenharmony_ci----------------------------------------------- 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ciA partir de la versión 4.15 del kernel Linux, se agregó un smp_mb() a 60362306a36Sopenharmony_ciREAD_ONCE() para DEC Alpha, lo que significa que las únicas personas que 60462306a36Sopenharmony_cinecesitan prestar atención a esta sección son aquellas que trabajan en el 60562306a36Sopenharmony_cicódigo específico de la arquitectura DEC Alpha y aquellas que trabajan en 60662306a36Sopenharmony_ciREAD_ONCE() por dentro. Para aquellos que lo necesitan, y para aquellos que 60762306a36Sopenharmony_ciestén interesados desde un punto de vista histórico, aquí está la historia 60862306a36Sopenharmony_cide las barreras de dependencia de dirección. 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci[!] Si bien las dependencias de direcciones se observan tanto en carga a 61162306a36Sopenharmony_cicarga como en relaciones de carga a store, las barreras de dependencia de 61262306a36Sopenharmony_cidirección no son necesarias para situaciones de carga a store. 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ciEl requisito de las barreras de dependencia de dirección es un poco sutil, 61562306a36Sopenharmony_ciy no siempre es obvio que sean necesarias. Para ilustrar, considere la 61662306a36Sopenharmony_cisiguiente secuencia de eventos: 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci CPU 1 CPU 2 61962306a36Sopenharmony_ci =============== =============== 62062306a36Sopenharmony_ci { A == 1, B == 2, C == 3, P == &A, Q == &C } 62162306a36Sopenharmony_ci B = 4; 62262306a36Sopenharmony_ci <barrera de escritura> 62362306a36Sopenharmony_ci WRITE_ONCE(P, &B); 62462306a36Sopenharmony_ci Q = READ_ONCE_OLD(P); 62562306a36Sopenharmony_ci D = *Q; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci[!] READ_ONCE_OLD() corresponde a READ_ONCE() del kernel anterior a 4.15, 62862306a36Sopenharmony_cique no implica una barrera de dependencia de direcciones. 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciHay una clara dependencia de dirección aquí, y parecería que al final de 63162306a36Sopenharmony_cila secuencia, Q debe ser &A o &B, y que: 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci (Q == &A) implica (D == 1) 63462306a36Sopenharmony_ci (Q == &B) implica (D == 4) 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci¡Pero! La percepción de la CPU 2 de P puede actualizarse _antes_ de su 63762306a36Sopenharmony_cipercepción de B, por lo tanto dando lugar a la siguiente situación: 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci (Q == &B) y (D == 2) ???? 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ciSi bien esto puede parecer una falla en el mantenimiento de la coherencia 64262306a36Sopenharmony_cio la causalidad, no lo es, y este comportamiento se puede observar en 64362306a36Sopenharmony_ciciertas CPU reales (como DEC Alfa). 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ciPara lidiar con esto, READ_ONCE() proporciona una barrera de dependencia 64662306a36Sopenharmony_cide dirección implícita desde el lanzamiento del kernel v4.15: 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci CPU 1 CPU 2 64962306a36Sopenharmony_ci =============== =============== 65062306a36Sopenharmony_ci { A == 1, B == 2, C == 3, P == &A, Q == &C } 65162306a36Sopenharmony_ci B = 4; 65262306a36Sopenharmony_ci <barrera de escritura> 65362306a36Sopenharmony_ci WRITE_ONCE(P, &B); 65462306a36Sopenharmony_ci Q = READ_ONCE(P); 65562306a36Sopenharmony_ci <barrera de dependencia de dirección implícita> 65662306a36Sopenharmony_ci D = *Q; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ciEsto refuerza la ocurrencia de una de las dos implicaciones, y previene la 65962306a36Sopenharmony_citercera posibilidad de surgir. 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci[!] Tenga en cuenta que esta situación extremadamente contraria a la 66362306a36Sopenharmony_ciintuición surge más fácilmente en máquinas con cachés divididos, de modo 66462306a36Sopenharmony_cique, por ejemplo, un banco de caché procesa líneas de caché pares y el otro 66562306a36Sopenharmony_cibanco procesa líneas impares de caché. El puntero P podría almacenarse en 66662306a36Sopenharmony_ciuna línea de caché impar y la variable B podría almacenarse en una línea de 66762306a36Sopenharmony_cicaché con número par. Entonces, si el banco de números pares de la memoria 66862306a36Sopenharmony_cicaché de la CPU de lectura está extremadamente ocupado mientras que el 66962306a36Sopenharmony_cibanco impar está inactivo, uno podría ver el nuevo valor del puntero P 67062306a36Sopenharmony_ci(&B), pero el antiguo valor de la variable B (2). 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ciNo se requiere una barrera de dependencia de dirección para ordenar 67462306a36Sopenharmony_ciescrituras dependientes porque las CPU que admite el kernel Linux no 67562306a36Sopenharmony_ciescriben hasta que están seguros (1) de que la escritura realmente 67662306a36Sopenharmony_cisucederá, (2) de la ubicación de la escritura, y (3) del valor a escribir. 67762306a36Sopenharmony_ciPero, por favor, lea atentamente la sección "DEPENDENCIAS DEL CONTROL" y el 67862306a36Sopenharmony_ciarchivo Documentation/RCU/rcu_dereference.rst: el compilador puede romperse 67962306a36Sopenharmony_ciy romper dependencias en muchas formas altamente creativas. 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci CPU 1 CPU 2 68262306a36Sopenharmony_ci =============== =============== 68362306a36Sopenharmony_ci { A == 1, B == 2, C = 3, P == &A, Q == &C } 68462306a36Sopenharmony_ci B = 4; 68562306a36Sopenharmony_ci <barrera de escritura> 68662306a36Sopenharmony_ci WRITE_ONCE(P, &B); 68762306a36Sopenharmony_ci Q = READ_ONCE_OLD(P); 68862306a36Sopenharmony_ci WRITE_ONCE(*Q, 5); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ciPor lo tanto, no se requiere ninguna barrera de dependencia de direcciones 69162306a36Sopenharmony_cipara ordenar la lectura en Q con el load en *Q. En otras palabras, este 69262306a36Sopenharmony_ciresultado está prohibido, incluso sin una barrera de dependencia de 69362306a36Sopenharmony_cidirección implícita del READ_ONCE() moderno: 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci (Q == &B) && (B == 4) 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ciTenga en cuenta que este patrón debe ser raro. Después de todo, el objetivo 69862306a36Sopenharmony_cidel orden de dependencia es -prevenir- escrituras en la estructura de 69962306a36Sopenharmony_cidatos, junto con los costosos errores de caché asociados con tales 70062306a36Sopenharmony_ciescrituras. Este patrón se puede utilizar para registrar raras condiciones 70162306a36Sopenharmony_cide error y similares, y el orden natural de las CPUs evita que se pierdan 70262306a36Sopenharmony_citales registros. 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ciTenga en cuenta que el orden proporcionado por una dependencia de dirección 70662306a36Sopenharmony_cies local para la CPU que lo contiene. Lea la sección sobre "Atomicidad 70762306a36Sopenharmony_cimulticopia" para más información. 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ciLa barrera de dependencia de dirección es muy importante para el sistema 71162306a36Sopenharmony_ciRCU, por ejemplo. Vea rcu_assign_pointer() y rcu_dereference() en 71262306a36Sopenharmony_ciinclude/linux/rcupdate.h. Esto permite que el objetivo actual de un puntero 71362306a36Sopenharmony_ciRCU sea reemplazado con un nuevo objetivo modificado, sin que el objetivo 71462306a36Sopenharmony_cidel reemplazo parezca estar inicializado de manera incompleta. 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ciConsulte también la subsección sobre "Coherencia de caché" para obtener un 71762306a36Sopenharmony_ciejemplo más completo. 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciDEPENDENCIAS DE CONTROL 72062306a36Sopenharmony_ci----------------------- 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ciLas dependencias de control pueden ser un poco complicadas porque los 72362306a36Sopenharmony_cicompiladores actuales no las entienden. El propósito de esta sección es 72462306a36Sopenharmony_ciayudarle a prevenir que la ignorancia del compilador rompa su código. 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ciUna dependencia de control load-load (de carga a carga) requiere una 72762306a36Sopenharmony_cibarrera de memoria de lectura completa, no simplemente una barrera 72862306a36Sopenharmony_ci(implícita) de dependencia de direcciones para que funcione correctamente. 72962306a36Sopenharmony_ciConsidere el siguiente fragmento de código: 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci q = READ_ONCE(a); 73262306a36Sopenharmony_ci <barrera implícita de dependencia de direcciones> 73362306a36Sopenharmony_ci if (q) { 73462306a36Sopenharmony_ci /* BUG: No hay dependencia de dirección!!! */ 73562306a36Sopenharmony_ci p = READ_ONCE(b); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ciEsto no tendrá el efecto deseado porque no hay una dependencia de dirección 73962306a36Sopenharmony_cireal, sino más bien una dependencia de control que la CPU puede 74062306a36Sopenharmony_cicortocircuitar al intentar predecir el resultado por adelantado, para que 74162306a36Sopenharmony_ciotras CPU vean la carga de b como si hubiera ocurrido antes que la carga de 74262306a36Sopenharmony_cia. En cuyo caso lo que realmente se requiere es: 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci q = READ_ONCE(a); 74562306a36Sopenharmony_ci if (q) { 74662306a36Sopenharmony_ci <barrera de lectura> 74762306a36Sopenharmony_ci p = READ_ONCE(b); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ciSin embargo, los stores no se especulan. Esto significa que ordenar -es- 75162306a36Sopenharmony_ciprovisto para dependencias de control de load-store, como en el siguiente 75262306a36Sopenharmony_ciejemplo: 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci q = READ_ONCE(a); 75562306a36Sopenharmony_ci if (q) { 75662306a36Sopenharmony_ci WRITE_ONCE(b, 1); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ciLas dependencias de control se emparejan normalmente con otros tipos de 76062306a36Sopenharmony_cibarreras. Dicho esto, tenga en cuenta que ni READ_ONCE() ni WRITE_ONCE() 76162306a36Sopenharmony_cison opcionales! Sin READ_ONCE(), el compilador podría combinar la carga de 76262306a36Sopenharmony_ci'a' con otras cargas de 'a'. Sin WRITE_ONCE(), el compilador podría 76362306a36Sopenharmony_cicombinar el store de 'b' con otros stores de 'b'. Cualquiera de estos casos 76462306a36Sopenharmony_cipuede dar lugar a efectos en el orden muy contrarios a la intuición. 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciPeor aún, si el compilador puede probar (decir) que el valor de la 76762306a36Sopenharmony_civariable 'a' siempre es distinta de cero, estaría dentro de sus derechos 76862306a36Sopenharmony_cipara optimizar el ejemplo original eliminando la declaración "if", como: 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci q = a; 77162306a36Sopenharmony_ci b = 1; /* BUG: Compilador y CPU pueden ambos reordernar!!! */ 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ciAsí que no deje de lado READ_ONCE(). 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ciEs tentador tratar de hacer cumplir el orden en stores idénticos en ambos 77662306a36Sopenharmony_cicaminos del "if" de la siguiente manera: 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci q = READ_ONCE(a); 77962306a36Sopenharmony_ci if (q) { 78062306a36Sopenharmony_ci barrier(); 78162306a36Sopenharmony_ci WRITE_ONCE(b, 1); 78262306a36Sopenharmony_ci hacer_algo(); 78362306a36Sopenharmony_ci } else { 78462306a36Sopenharmony_ci barrier(); 78562306a36Sopenharmony_ci WRITE_ONCE(b, 1); 78662306a36Sopenharmony_ci hacer_otra_cosa(); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ciDesafortunadamente, los compiladores actuales transformarán esto de la 79062306a36Sopenharmony_cisiguiente manera en casos de alto nivel de optimización: 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci q = READ_ONCE(a); 79362306a36Sopenharmony_ci barrier(); 79462306a36Sopenharmony_ci WRITE_ONCE(b, 1); /* BUG: No hay orden en load de a!!! */ 79562306a36Sopenharmony_ci if (q) { 79662306a36Sopenharmony_ci /* WRITE_ONCE(b, 1); -- movido arriba, BUG!!! */ 79762306a36Sopenharmony_ci hacer_algo(); 79862306a36Sopenharmony_ci } else { 79962306a36Sopenharmony_ci /* WRITE_ONCE(b, 1); -- movido arriba, BUG!!! */ 80062306a36Sopenharmony_ci hacer_otra_cosa(); 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ciAhora no hay condicional entre la carga de 'a' y el store de 'b', lo que 80462306a36Sopenharmony_cisignifica que la CPU está en su derecho de reordenarlos: El condicional es 80562306a36Sopenharmony_ciabsolutamente necesario y debe estar presente en el código ensamblador 80662306a36Sopenharmony_ciincluso después de que se hayan aplicado todas las optimizaciones del 80762306a36Sopenharmony_cicompilador. Por lo tanto, si necesita ordenar en este ejemplo, necesita 80862306a36Sopenharmony_ciexplícitamente barreras de memoria, por ejemplo, smp_store_release(): 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci q = READ_ONCE(a); 81262306a36Sopenharmony_ci if (q) { 81362306a36Sopenharmony_ci smp_store_release(&b, 1); 81462306a36Sopenharmony_ci hacer_algo(); 81562306a36Sopenharmony_ci } else { 81662306a36Sopenharmony_ci smp_store_release(&b, 1); 81762306a36Sopenharmony_ci hacer_otra_cosa(); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ciPor el contrario, sin barreras de memoria explícita, el control de un if 82162306a36Sopenharmony_cicon dos opciones está garantizado solo cuando los stores difieren, por 82262306a36Sopenharmony_ciejemplo: 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci q = READ_ONCE(a); 82562306a36Sopenharmony_ci if (q) { 82662306a36Sopenharmony_ci WRITE_ONCE(b, 1); 82762306a36Sopenharmony_ci hacer_algo(); 82862306a36Sopenharmony_ci } else { 82962306a36Sopenharmony_ci WRITE_ONCE(b, 2); 83062306a36Sopenharmony_ci hacer_otra_cosa(); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ciAún se requiere el inicial READ_ONCE() para evitar que el compilador toque 83462306a36Sopenharmony_ciel valor de 'a'. 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ciAdemás, debe tener cuidado con lo que hace con la variable local 'q', de lo 83762306a36Sopenharmony_cicontrario, el compilador podría adivinar el valor y volver a eliminar el 83862306a36Sopenharmony_cinecesario condicional. Por ejemplo: 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci q = READ_ONCE(a); 84162306a36Sopenharmony_ci if (q % MAX) { 84262306a36Sopenharmony_ci WRITE_ONCE(b, 1); 84362306a36Sopenharmony_ci hacer_algo(); 84462306a36Sopenharmony_ci } else { 84562306a36Sopenharmony_ci WRITE_ONCE(b, 2); 84662306a36Sopenharmony_ci hacer_otra_cosa(); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ciSi MAX se define como 1, entonces el compilador sabe que (q % MAX) es igual 85062306a36Sopenharmony_cia cero, en cuyo caso el compilador tiene derecho a transformar el código 85162306a36Sopenharmony_cianterior en el siguiente: 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci q = READ_ONCE(a); 85462306a36Sopenharmony_ci WRITE_ONCE(b, 2); 85562306a36Sopenharmony_ci hacer_otra_cosa(); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ciDada esta transformación, la CPU no está obligada a respetar el orden entre 85862306a36Sopenharmony_cila carga de la variable 'a' y el store de la variable 'b'. Es tentador 85962306a36Sopenharmony_ciagregar una barrier(), pero esto no ayuda. El condicional se ha ido, y la 86062306a36Sopenharmony_cibarrera no lo traerá de vuelta. Por lo tanto, si confia en este orden, debe 86162306a36Sopenharmony_ciasegurarse de que MAX sea mayor que uno, tal vez de la siguiente manera: 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci q = READ_ONCE(a); 86462306a36Sopenharmony_ci BUILD_BUG_ON(MAX <= 1); /* Orden de carga de a con store de b */ 86562306a36Sopenharmony_ci if (q % MAX) { 86662306a36Sopenharmony_ci WRITE_ONCE(b, 1); 86762306a36Sopenharmony_ci hacer_algo(); 86862306a36Sopenharmony_ci } else { 86962306a36Sopenharmony_ci WRITE_ONCE(b, 2); 87062306a36Sopenharmony_ci hacer_otra_cosa(); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciTenga en cuenta una vez más que los stores de 'b' difieren. Si fueran 87462306a36Sopenharmony_ciidénticos, como se señaló anteriormente, el compilador podría sacar ese 87562306a36Sopenharmony_cistore fuera de la declaración 'if'. 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciTambién debe tener cuidado de no confiar demasiado en el cortocircuito 87862306a36Sopenharmony_cide la evaluación booleana. Considere este ejemplo: 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci q = READ_ONCE(a); 88162306a36Sopenharmony_ci if (q || 1 > 0) 88262306a36Sopenharmony_ci WRITE_ONCE(b, 1); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ciDebido a que la primera condición no puede fallar y la segunda condición es 88562306a36Sopenharmony_cisiempre cierta, el compilador puede transformar este ejemplo de la 88662306a36Sopenharmony_cisiguiente manera, rompiendo la dependencia del control: 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci q = READ_ONCE(a); 88962306a36Sopenharmony_ci WRITE_ONCE(b, 1); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ciEste ejemplo subraya la necesidad de asegurarse de que el compilador no 89262306a36Sopenharmony_cipueda adivinar su código. Más generalmente, aunque READ_ONCE() fuerza 89362306a36Sopenharmony_cial compilador para emitir código para una carga dada, no fuerza al 89462306a36Sopenharmony_cicompilador para usar los resultados. 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ciAdemás, las dependencias de control se aplican solo a la cláusula then y 89762306a36Sopenharmony_cila cláusula else de la sentencia if en cuestión. En particular, no se 89862306a36Sopenharmony_ciaplica necesariamente al código que sigue a la declaración if: 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci q = READ_ONCE(a); 90162306a36Sopenharmony_ci if (q) { 90262306a36Sopenharmony_ci WRITE_ONCE(b, 1); 90362306a36Sopenharmony_ci } else { 90462306a36Sopenharmony_ci WRITE_ONCE(b, 2); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci WRITE_ONCE(c, 1); /* BUG: No hay orden para la lectura de 'a'. */ 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ciEs tentador argumentar que, de hecho, existe un orden porque el compilador 90962306a36Sopenharmony_cino puede reordenar accesos volátiles y tampoco puede reordenar escrituras 91062306a36Sopenharmony_cien 'b' con la condición. Desafortunadamente para esta línea de 91162306a36Sopenharmony_cirazonamiento, el compilador podría compilar las dos escrituras en 'b' como 91262306a36Sopenharmony_ciinstrucciones de movimiento condicional, como en este fantástico idioma 91362306a36Sopenharmony_cipseudo-ensamblador: 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci ld r1,a 91662306a36Sopenharmony_ci cmp r1,$0 91762306a36Sopenharmony_ci cmov,ne r4,$1 91862306a36Sopenharmony_ci cmov,eq r4,$2 91962306a36Sopenharmony_ci st r4,b 92062306a36Sopenharmony_ci st $1,c 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ciUna CPU débilmente ordenada no tendría dependencia de ningún tipo entre la 92362306a36Sopenharmony_cicarga de 'a' y el store de 'c'. Las dependencias de control se extenderían 92462306a36Sopenharmony_cisolo al par de instrucciones cmov y el store dependiente de ellas. En 92562306a36Sopenharmony_ciresumen, las dependencias de control se aplican solo a los stores en la 92662306a36Sopenharmony_cicláusula then y la cláusula else de la sentencia if en cuestión (incluidas 92762306a36Sopenharmony_cilas funciones invocado por esas dos cláusulas), no al código que sigue a 92862306a36Sopenharmony_ciesa declaración if. 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ciTenga muy en cuenta que el orden proporcionado por una dependencia de 93262306a36Sopenharmony_cicontrol es local a la CPU que lo contiene. Vea el apartado de "Atomicidad 93362306a36Sopenharmony_cimulticopia" para más información. 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ciEn resumen: 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci (*) Las dependencias de control pueden ordenar cargas anteriores para 93962306a36Sopenharmony_ci stores posteriores. Sin embargo, no garantizan ningún otro tipo de 94062306a36Sopenharmony_ci orden: No cargas previas contra cargas posteriores, ni 94162306a36Sopenharmony_ci almacenamientos previos y luego nada. Si necesita tales formas de 94262306a36Sopenharmony_ci orden, use smp_rmb(), smp_wmb() o, en el caso de stores anteriores y 94362306a36Sopenharmony_ci cargas posteriores, smp_mb(). 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci (*) Si ambos caminos de la declaración "if" comienzan con stores 94662306a36Sopenharmony_ci idénticos de la misma variable, entonces esos stores deben ser 94762306a36Sopenharmony_ci ordenados, ya sea precediéndoles a ambos con smp_mb() o usando 94862306a36Sopenharmony_ci smp_store_release() para realizar el store. Tenga en cuenta que -no- 94962306a36Sopenharmony_ci es suficiente usar barrier() al comienzo de cada caso de la 95062306a36Sopenharmony_ci declaración "if" porque, como se muestra en el ejemplo anterior, la 95162306a36Sopenharmony_ci optimización de los compiladores puede destruir la dependencia de 95262306a36Sopenharmony_ci control respetando al pie de la letra la ley de barrier(). 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci (*) Las dependencias de control requieren al menos un condicional en 95562306a36Sopenharmony_ci tiempo de ejecución entre la carga anterior y el almacenamiento 95662306a36Sopenharmony_ci posterior, y este condicional debe implicar la carga previa. Si el 95762306a36Sopenharmony_ci compilador es capaz de optimizar el condicional y quitarlo, también 95862306a36Sopenharmony_ci habrá optimizado el ordenar. El uso cuidadoso de READ_ONCE() y 95962306a36Sopenharmony_ci WRITE_ONCE() puede ayudar a preservar el necesario condicional. 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci (*) Las dependencias de control requieren que el compilador evite 96262306a36Sopenharmony_ci reordenar las dependencia hasta su inexistencia. El uso cuidadoso de 96362306a36Sopenharmony_ci READ_ONCE() o atomic{,64}_read() puede ayudarle a preservar la 96462306a36Sopenharmony_ci dependencia de control. Consulte la sección BARRERA DEL COMPILADOR 96562306a36Sopenharmony_ci para obtener más información al respecto. 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci (*) Las dependencias de control se aplican solo a la cláusula then y la 96862306a36Sopenharmony_ci cláusula else de la sentencia "if" que contiene la dependencia de 96962306a36Sopenharmony_ci control, incluyendo cualquier función a la que llamen dichas dos 97062306a36Sopenharmony_ci cláusulas. Las dependencias de control no se aplican al código que 97162306a36Sopenharmony_ci sigue a la instrucción if que contiene la dependencia de control. 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci (*) Las dependencias de control se emparejan normalmente con otros tipos 97462306a36Sopenharmony_ci de barreras. 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci (*) Las dependencias de control no proporcionan atomicidad multicopia. Si 97762306a36Sopenharmony_ci usted necesita todas las CPU para ver un store dado al mismo tiempo, 97862306a36Sopenharmony_ci emplee smp_mb(). 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci (*) Los compiladores no entienden las dependencias de control. Por lo 98162306a36Sopenharmony_ci tanto es su trabajo asegurarse de que no rompan su código. 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ciEMPAREJAMIENTO DE BARRERAS SMP 98562306a36Sopenharmony_ci------------------------------ 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ciCuando se trata de interacciones CPU-CPU, ciertos tipos de barrera de 98862306a36Sopenharmony_cimemoria deben estar siempre emparejados. La falta del apropiado 98962306a36Sopenharmony_ciemparejamiento es casi seguro un error. 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ciLas barreras generales se emparejan entre sí, aunque también se emparejan 99262306a36Sopenharmony_cicon la mayoría de otro tipo de barreras, aunque sin atomicidad multicopia. 99362306a36Sopenharmony_ciUna barrera de adquisición se empareja con una barrera de liberación, pero 99462306a36Sopenharmony_ciambas también pueden emparejarse con otras barreras, incluidas, por 99562306a36Sopenharmony_cisupuesto, las barreras generales. Una barrera de escritura se empareja con 99662306a36Sopenharmony_ciuna barrera de dependencia de dirección, una dependencia de control, una 99762306a36Sopenharmony_cibarrera de adquisición, una barrera de liberación, una barrera de lectura 99862306a36Sopenharmony_cio una barrera general. Del mismo modo, una barrera de lectura se empareja 99962306a36Sopenharmony_cicon una de dependencia de control o barrera de dependencia de dirección con 100062306a36Sopenharmony_ciuna barrera de escritura, una barrera de adquisición, una barrera de 100162306a36Sopenharmony_ciliberación o una barrera general: 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci CPU 1 CPU 2 100462306a36Sopenharmony_ci =============== =============== 100562306a36Sopenharmony_ci WRITE_ONCE(a, 1); 100662306a36Sopenharmony_ci <barrera de escritura> 100762306a36Sopenharmony_ci WRITE_ONCE(b, 2); x = READ_ONCE(b); 100862306a36Sopenharmony_ci <barrera de lectura> 100962306a36Sopenharmony_ci y = READ_ONCE(a); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ciO bien: 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci CPU 1 CPU 2 101462306a36Sopenharmony_ci =============== =============================== 101562306a36Sopenharmony_ci a = 1; 101662306a36Sopenharmony_ci <barrera de escritura> 101762306a36Sopenharmony_ci WRITE_ONCE(b, &a); x = READ_ONCE(b); 101862306a36Sopenharmony_ci <barrera de dependencia de dirección implícita> 101962306a36Sopenharmony_ci y = *x; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ciO incluso: 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci CPU 1 CPU 2 102462306a36Sopenharmony_ci =============== =============================== 102562306a36Sopenharmony_ci r1 = READ_ONCE(y); 102662306a36Sopenharmony_ci <barrera general> 102762306a36Sopenharmony_ci WRITE_ONCE(x, 1); if (r2 = READ_ONCE(x)) { 102862306a36Sopenharmony_ci <barrera de control implícita> 102962306a36Sopenharmony_ci WRITE_ONCE(y, 1); 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci assert(r1 == 0 || r2 == 0); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ciBásicamente, la barrera de lectura siempre tiene que estar ahí, aunque 103562306a36Sopenharmony_cipuede ser del tipo "más débil". 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci[!] Tenga en cuenta que normalmente se esperaría que los stores antes de la 103862306a36Sopenharmony_cibarrera de escritura se hagan coincidir con los stores después de la 103962306a36Sopenharmony_cibarrera de lectura o la barrera de dependencia de dirección, y viceversa: 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci CPU 1 CPU 2 104262306a36Sopenharmony_ci =================== =================== 104362306a36Sopenharmony_ci WRITE_ONCE(a, 1); }---- --->{ v = READ_ONCE(c); 104462306a36Sopenharmony_ci WRITE_ONCE(b, 2); } \ / { w = READ_ONCE(d); 104562306a36Sopenharmony_ci <barrera de escritura> \ <barrera de lectura> 104662306a36Sopenharmony_ci WRITE_ONCE(c, 3); } / \ { x = READ_ONCE(a); 104762306a36Sopenharmony_ci WRITE_ONCE(d, 4); }---- --->{ y = READ_ONCE(b); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ciEJEMPLOS DE SECUENCIAS DE BARRERA DE MEMORIA 105162306a36Sopenharmony_ci-------------------------------------------- 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ciEn primer lugar, las barreras de escritura actúan como orden parcial en las 105462306a36Sopenharmony_cioperaciones de store. Considere la siguiente secuencia de eventos: 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci CPU 1 105762306a36Sopenharmony_ci ======================= 105862306a36Sopenharmony_ci STORE A = 1 105962306a36Sopenharmony_ci STORE B = 2 106062306a36Sopenharmony_ci STORE C = 3 106162306a36Sopenharmony_ci <barrera de escritura> 106262306a36Sopenharmony_ci STORE D = 4 106362306a36Sopenharmony_ci STORE E = 5 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ciEsta secuencia de eventos es finalizado para con el sistema de coherencia 106662306a36Sopenharmony_cide memoria en un orden que el resto del sistema podría percibir como el 106762306a36Sopenharmony_ciconjunto desordenado { STORE A, STORE B, STORE C} todo ocurriendo antes del 106862306a36Sopenharmony_ciconjunto desordenado { STORE D, STORE E}: 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci +-------+ : : 107262306a36Sopenharmony_ci | | +------+ 107362306a36Sopenharmony_ci | |------>| C=3 | } /\ 107462306a36Sopenharmony_ci | | : +------+ }----- \ -----> Eventos perceptibles para 107562306a36Sopenharmony_ci | | : | A=1 | } \/ el resto del sistema 107662306a36Sopenharmony_ci | | : +------+ } 107762306a36Sopenharmony_ci | CPU 1 | : | B=2 | } 107862306a36Sopenharmony_ci | | +------+ } 107962306a36Sopenharmony_ci | | wwwwwwwwwwwwwwww } <--- En este momento la barrera de 108062306a36Sopenharmony_ci | | +------+ } escritura requiere que todos los 108162306a36Sopenharmony_ci | | : | E=5 | } stores anteriores a la barrera 108262306a36Sopenharmony_ci | | : +------+ } sean confirmados antes de que otros 108362306a36Sopenharmony_ci | |------>| D=4 | } store puedan suceder 108462306a36Sopenharmony_ci | | +------+ 108562306a36Sopenharmony_ci +-------+ : : 108662306a36Sopenharmony_ci | 108762306a36Sopenharmony_ci | Secuencia por la cual los stores son confirmados al 108862306a36Sopenharmony_ci | sistema de memoria por parte del CPU 1 108962306a36Sopenharmony_ci V 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ciEn segundo lugar, las barreras de dependencia de dirección actúan como 109262306a36Sopenharmony_ciórdenes parciales sobre la dirección de cargas dependientes. Considere la 109362306a36Sopenharmony_cisiguiente secuencia de eventos: 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci CPU 1 CPU 2 109662306a36Sopenharmony_ci ======================= ======================= 109762306a36Sopenharmony_ci { B = 7; X = 9; Y = 8; C = &Y } 109862306a36Sopenharmony_ci STORE A = 1 109962306a36Sopenharmony_ci STORE B = 2 110062306a36Sopenharmony_ci <barrera de escritura> 110162306a36Sopenharmony_ci STORE C = &B LOAD X 110262306a36Sopenharmony_ci STORE D = 4 LOAD C (consigue &B) 110362306a36Sopenharmony_ci LOAD *C (lee B) 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ciSin intervención, la CPU 2 puede percibir los eventos en la CPU 1 en orden 110662306a36Sopenharmony_cialeatorio a efectos prácticos, a pesar de la barrera de escritura emitida 110762306a36Sopenharmony_cipor la CPU 1: 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci +-------+ : : : : 111062306a36Sopenharmony_ci | | +------+ +-------+ | Secuencia de 111162306a36Sopenharmony_ci | |------>| B=2 |----- --->| Y->8 | | actualizado de 111262306a36Sopenharmony_ci | | : +------+ \ +-------+ | percepción en CPU 2 111362306a36Sopenharmony_ci | CPU 1 | : | A=1 | \ --->| C->&Y | V 111462306a36Sopenharmony_ci | | +------+ | +-------+ 111562306a36Sopenharmony_ci | | wwwwwwwwwwwwwwww | : : 111662306a36Sopenharmony_ci | | +------+ | : : 111762306a36Sopenharmony_ci | | : | C=&B |--- | : : +-------+ 111862306a36Sopenharmony_ci | | : +------+ \ | +-------+ | | 111962306a36Sopenharmony_ci | |------>| D=4 | ----------->| C->&B |------>| | 112062306a36Sopenharmony_ci | | +------+ | +-------+ | | 112162306a36Sopenharmony_ci +-------+ : : | : : | | 112262306a36Sopenharmony_ci | : : | | 112362306a36Sopenharmony_ci | : : | CPU 2 | 112462306a36Sopenharmony_ci | +-------+ | | 112562306a36Sopenharmony_ci Percepción de B ---> | | B->7 |------>| | 112662306a36Sopenharmony_ci aparentemente incorrecta! | +-------+ | | 112762306a36Sopenharmony_ci | : : | | 112862306a36Sopenharmony_ci | +-------+ | | 112962306a36Sopenharmony_ci La carga de X frena ---> \ | X->9 |------>| | 113062306a36Sopenharmony_ci el mantenimiento de \ +-------+ | | 113162306a36Sopenharmony_ci la coherencia de B ----->| B->2 | +-------+ 113262306a36Sopenharmony_ci +-------+ 113362306a36Sopenharmony_ci : : 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ciEn el ejemplo anterior, la CPU 2 percibe que B es 7, a pesar de la carga de 113762306a36Sopenharmony_ci*C (que sería B) viniendo después del LOAD de C. 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ciSin embargo, si se colocara una barrera de dependencia de dirección entre 114062306a36Sopenharmony_cila carga de C y la carga de *C (es decir: B) en la CPU 2: 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci CPU 1 CPU 2 114362306a36Sopenharmony_ci ======================= ======================= 114462306a36Sopenharmony_ci { B = 7; X = 9; Y = 8; C = &Y } 114562306a36Sopenharmony_ci STORE A = 1 114662306a36Sopenharmony_ci STORE B = 2 114762306a36Sopenharmony_ci <barrera de escritura> 114862306a36Sopenharmony_ci STORE C = &B LOAD X 114962306a36Sopenharmony_ci STORE D = 4 LOAD C (consigue &B) 115062306a36Sopenharmony_ci <barrera de dependencia de dirección> 115162306a36Sopenharmony_ci LOAD *C (reads B) 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cientonces ocurrirá lo siguiente: 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci +-------+ : : : : 115662306a36Sopenharmony_ci | | +------+ +-------+ 115762306a36Sopenharmony_ci | |------>| B=2 |----- --->| Y->8 | 115862306a36Sopenharmony_ci | | : +------+ \ +-------+ 115962306a36Sopenharmony_ci | CPU 1 | : | A=1 | \ --->| C->&Y | 116062306a36Sopenharmony_ci | | +------+ | +-------+ 116162306a36Sopenharmony_ci | | wwwwwwwwwwwwwwww | : : 116262306a36Sopenharmony_ci | | +------+ | : : 116362306a36Sopenharmony_ci | | : | C=&B |--- | : : +-------+ 116462306a36Sopenharmony_ci | | : +------+ \ | +-------+ | | 116562306a36Sopenharmony_ci | |------>| D=4 | ----------->| C->&B |------>| | 116662306a36Sopenharmony_ci | | +------+ | +-------+ | | 116762306a36Sopenharmony_ci +-------+ : : | : : | | 116862306a36Sopenharmony_ci | : : | | 116962306a36Sopenharmony_ci | : : | CPU 2 | 117062306a36Sopenharmony_ci | +-------+ | | 117162306a36Sopenharmony_ci | | X->9 |------>| | 117262306a36Sopenharmony_ci | +-------+ | | 117362306a36Sopenharmony_ci Se asegura de que ---> \ aaaaaaaaaaaaaaaaa | | 117462306a36Sopenharmony_ci los efectos anteriores al \ +-------+ | | 117562306a36Sopenharmony_ci store de C sean percibidos ----->| B->2 |------>| | 117662306a36Sopenharmony_ci por los siguientes loads +-------+ | | 117762306a36Sopenharmony_ci : : +-------+ 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ciY en tercer lugar, una barrera de lectura actúa como un orden parcial sobre 118162306a36Sopenharmony_cilas cargas. Considere la siguiente secuencia de eventos: 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci CPU 1 CPU 2 118462306a36Sopenharmony_ci ======================= ======================= 118562306a36Sopenharmony_ci { A = 0, B = 9 } 118662306a36Sopenharmony_ci STORE A=1 118762306a36Sopenharmony_ci <barrera de escritura> 118862306a36Sopenharmony_ci STORE B=2 118962306a36Sopenharmony_ci LOAD B 119062306a36Sopenharmony_ci LOAD A 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ciSin intervención, la CPU 2 puede elegir percibir los eventos en la CPU 1 en 119362306a36Sopenharmony_cialgún orden aleatorio a efectos prácticos, a pesar de la barrera de 119462306a36Sopenharmony_ciescritura emitida por la CPU 1: 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci +-------+ : : : : 119762306a36Sopenharmony_ci | | +------+ +-------+ 119862306a36Sopenharmony_ci | |------>| A=1 |------ --->| A->0 | 119962306a36Sopenharmony_ci | | +------+ \ +-------+ 120062306a36Sopenharmony_ci | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | 120162306a36Sopenharmony_ci | | +------+ | +-------+ 120262306a36Sopenharmony_ci | |------>| B=2 |--- | : : 120362306a36Sopenharmony_ci | | +------+ \ | : : +-------+ 120462306a36Sopenharmony_ci +-------+ : : \ | +-------+ | | 120562306a36Sopenharmony_ci ---------->| B->2 |------>| | 120662306a36Sopenharmony_ci | +-------+ | CPU 2 | 120762306a36Sopenharmony_ci | | A->0 |------>| | 120862306a36Sopenharmony_ci | +-------+ | | 120962306a36Sopenharmony_ci | : : +-------+ 121062306a36Sopenharmony_ci \ : : 121162306a36Sopenharmony_ci \ +-------+ 121262306a36Sopenharmony_ci ---->| A->1 | 121362306a36Sopenharmony_ci +-------+ 121462306a36Sopenharmony_ci : : 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ciSin embargo, si se colocara una barrera de lectura entre la carga de B y la 121762306a36Sopenharmony_cicarga de A en la CPU 2: 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci CPU 1 CPU 2 122062306a36Sopenharmony_ci ======================= ======================= 122162306a36Sopenharmony_ci { A = 0, B = 9 } 122262306a36Sopenharmony_ci STORE A=1 122362306a36Sopenharmony_ci <barrera de escritura> 122462306a36Sopenharmony_ci STORE B=2 122562306a36Sopenharmony_ci LOAD B 122662306a36Sopenharmony_ci <barrera de lectura> 122762306a36Sopenharmony_ci LOAD A 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cientonces el orden parcial impuesto por la CPU 1 será percibido 123062306a36Sopenharmony_cicorrectamente por la CPU 2: 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci +-------+ : : : : 123362306a36Sopenharmony_ci | | +------+ +-------+ 123462306a36Sopenharmony_ci | |------>| A=1 |------ --->| A->0 | 123562306a36Sopenharmony_ci | | +------+ \ +-------+ 123662306a36Sopenharmony_ci | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | 123762306a36Sopenharmony_ci | | +------+ | +-------+ 123862306a36Sopenharmony_ci | |------>| B=2 |--- | : : 123962306a36Sopenharmony_ci | | +------+ \ | : : +-------+ 124062306a36Sopenharmony_ci +-------+ : : \ | +-------+ | | 124162306a36Sopenharmony_ci ---------->| B->2 |------>| | 124262306a36Sopenharmony_ci | +-------+ | CPU 2 | 124362306a36Sopenharmony_ci | : : | | 124462306a36Sopenharmony_ci | : : | | 124562306a36Sopenharmony_ci En este punto la barrera ----> \ rrrrrrrrrrrrrrrrr | | 124662306a36Sopenharmony_ci de lectura consigue que \ +-------+ | | 124762306a36Sopenharmony_ci todos los efectos anteriores ---->| A->1 |------>| | 124862306a36Sopenharmony_ci al almacenamiento de B sean +-------+ | | 124962306a36Sopenharmony_ci perceptibles por la CPU 2 : : +-------+ 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ciPara ilustrar esto de manera más completa, considere lo que podría pasar si 125362306a36Sopenharmony_ciel código contenía una carga de A a cada lado de la barrera de lectura: 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci CPU 1 CPU 2 125662306a36Sopenharmony_ci ======================= ======================= 125762306a36Sopenharmony_ci { A = 0, B = 9 } 125862306a36Sopenharmony_ci STORE A=1 125962306a36Sopenharmony_ci <barrera de escritura> 126062306a36Sopenharmony_ci STORE B=2 126162306a36Sopenharmony_ci LOAD B 126262306a36Sopenharmony_ci LOAD A [primer load de A] 126362306a36Sopenharmony_ci <rbarrera de lectura> 126462306a36Sopenharmony_ci LOAD A [segundo load de A] 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ciAunque las dos cargas de A ocurren después de la carga de B, ambas pueden 126762306a36Sopenharmony_ciobtener diferentes valores: 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci +-------+ : : : : 127062306a36Sopenharmony_ci | | +------+ +-------+ 127162306a36Sopenharmony_ci | |------>| A=1 |------ --->| A->0 | 127262306a36Sopenharmony_ci | | +------+ \ +-------+ 127362306a36Sopenharmony_ci | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | 127462306a36Sopenharmony_ci | | +------+ | +-------+ 127562306a36Sopenharmony_ci | |------>| B=2 |--- | : : 127662306a36Sopenharmony_ci | | +------+ \ | : : +-------+ 127762306a36Sopenharmony_ci +-------+ : : \ | +-------+ | | 127862306a36Sopenharmony_ci ---------->| B->2 |------>| | 127962306a36Sopenharmony_ci | +-------+ | CPU 2 | 128062306a36Sopenharmony_ci | : : | | 128162306a36Sopenharmony_ci | : : | | 128262306a36Sopenharmony_ci | +-------+ | | 128362306a36Sopenharmony_ci | | A->0 |------>| 1st | 128462306a36Sopenharmony_ci | +-------+ | | 128562306a36Sopenharmony_ci En este punto la barrera ----> \ rrrrrrrrrrrrrrrrr | | 128662306a36Sopenharmony_ci de lectura consigue que \ +-------+ | | 128762306a36Sopenharmony_ci todos los efectos anteriores ---->| A->1 |------>| | 128862306a36Sopenharmony_ci al almacenamiento de B sean +-------+ | | 128962306a36Sopenharmony_ci perceptibles por la CPU 2 : : +-------+ 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ciPero puede ser que la actualización a A desde la CPU 1 se vuelva 129262306a36Sopenharmony_ciperceptible para la CPU 2 antes de que la barrera de lectura se complete de 129362306a36Sopenharmony_citodos modos: 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci +-------+ : : : : 129662306a36Sopenharmony_ci | | +------+ +-------+ 129762306a36Sopenharmony_ci | |------>| A=1 |------ --->| A->0 | 129862306a36Sopenharmony_ci | | +------+ \ +-------+ 129962306a36Sopenharmony_ci | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | 130062306a36Sopenharmony_ci | | +------+ | +-------+ 130162306a36Sopenharmony_ci | |------>| B=2 |--- | : : 130262306a36Sopenharmony_ci | | +------+ \ | : : +-------+ 130362306a36Sopenharmony_ci +-------+ : : \ | +-------+ | | 130462306a36Sopenharmony_ci ---------->| B->2 |------>| | 130562306a36Sopenharmony_ci | +-------+ | CPU 2 | 130662306a36Sopenharmony_ci | : : | | 130762306a36Sopenharmony_ci \ : : | | 130862306a36Sopenharmony_ci \ +-------+ | | 130962306a36Sopenharmony_ci ---->| A->1 |------>| 1st | 131062306a36Sopenharmony_ci +-------+ | | 131162306a36Sopenharmony_ci rrrrrrrrrrrrrrrrr | | 131262306a36Sopenharmony_ci +-------+ | | 131362306a36Sopenharmony_ci | A->1 |------>| 2nd | 131462306a36Sopenharmony_ci +-------+ | | 131562306a36Sopenharmony_ci : : +-------+ 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ciLa garantía es que la segunda carga siempre dará como resultado A == 1 si 131862306a36Sopenharmony_cila carga de B resultó en B == 2. No existe tal garantía para la primera 131962306a36Sopenharmony_cicarga de A; esto puede dar como resultado A == 0 o A == 1. 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ciBARRERAS DE MEMORIA DE LECTURA FRENTE A ESPECULACIÓN DE CARGA 132362306a36Sopenharmony_ci------------------------------------------------------------- 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ciMuchas CPU especulan con las cargas: es decir, ven que necesitarán cargar 132662306a36Sopenharmony_ciun elemento de la memoria, y encuentran un momento en el que no están 132762306a36Sopenharmony_ciusando el bus para ningún otra carga, y también en la carga por adelantado, 132862306a36Sopenharmony_ciaunque en realidad no lo hayan llegado a ese punto en el flujo de ejecución 132962306a36Sopenharmony_cide instrucciones todavía. Esto permite que la instrucción de carga real 133062306a36Sopenharmony_cipotencialmente complete de inmediato, porque la CPU ya tiene el valor a 133162306a36Sopenharmony_cimano. 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ciPuede resultar que la CPU en realidad no necesitara el valor, tal vez 133462306a36Sopenharmony_ciporque una condición eludió la carga, en cuyo caso puede descartar el valor 133562306a36Sopenharmony_cio simplemente almacenar en caché para su uso posterior. 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ciConsidere: 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci CPU 1 CPU 2 134062306a36Sopenharmony_ci ======================= ======================= 134162306a36Sopenharmony_ci LOAD B 134262306a36Sopenharmony_ci DIVIDE } Instrucciones de división 134362306a36Sopenharmony_ci DIVIDE } tardan mucho en terminar 134462306a36Sopenharmony_ci LOAD A 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cidonde DIVIDE es DIVIDIR. Que podría aparecer como esto: 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci : : +-------+ 134962306a36Sopenharmony_ci +-------+ | | 135062306a36Sopenharmony_ci --->| B->2 |------>| | 135162306a36Sopenharmony_ci +-------+ | CPU 2 | 135262306a36Sopenharmony_ci : :DIVIDE | | 135362306a36Sopenharmony_ci +-------+ | | 135462306a36Sopenharmony_ci La CPU ocupada con la división ---> --->| A->0 |~~~~ | | 135562306a36Sopenharmony_ci especula sobre el LOAD de A +-------+ ~ | | 135662306a36Sopenharmony_ci : : ~ | | 135762306a36Sopenharmony_ci : :DIVIDE | | 135862306a36Sopenharmony_ci : : ~ | | 135962306a36Sopenharmony_ci Una vez completadas las divisiones --> : : ~-->| | 136062306a36Sopenharmony_ci la CPU puede realizar el : : | | 136162306a36Sopenharmony_ci LOAD con efecto inmediato : : +-------+ 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ciColocando una barrera de lectura o una barrera de dependencia de dirección 136562306a36Sopenharmony_cijusto antes de la segundo carga: 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci CPU 1 CPU 2 137062306a36Sopenharmony_ci ======================= ======================= 137162306a36Sopenharmony_ci LOAD B 137262306a36Sopenharmony_ci DIVIDE 137362306a36Sopenharmony_ci DIVIDE 137462306a36Sopenharmony_ci <rbarrera de lectura> 137562306a36Sopenharmony_ci LOAD A 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ciobligará a reconsiderar cualquier valor obtenido especulativamente en una 137862306a36Sopenharmony_cimedida dependiente del tipo de barrera utilizada. Si no se hizo ningún 137962306a36Sopenharmony_cicambio en la ubicación de memoria especulada, entonces el valor especulado 138062306a36Sopenharmony_cisolo se usará: 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci : : +-------+ 138362306a36Sopenharmony_ci +-------+ | | 138462306a36Sopenharmony_ci --->| B->2 |------>| | 138562306a36Sopenharmony_ci +-------+ | CPU 2 | 138662306a36Sopenharmony_ci : :DIVIDE | | 138762306a36Sopenharmony_ci +-------+ | | 138862306a36Sopenharmony_ci La CPU ocupada con la división ---> --->| A->0 |~~~~ | | 138962306a36Sopenharmony_ci especula sobre el LOAD de A +-------+ ~ | | 139062306a36Sopenharmony_ci : : ~ | | 139162306a36Sopenharmony_ci : :DIVIDE | | 139262306a36Sopenharmony_ci : : ~ | | 139362306a36Sopenharmony_ci : : ~ | | 139462306a36Sopenharmony_ci rrrrrrrrrrrrrrrr~ | | 139562306a36Sopenharmony_ci : : ~ | | 139662306a36Sopenharmony_ci : : ~-->| | 139762306a36Sopenharmony_ci : : | | 139862306a36Sopenharmony_ci : : +-------+ 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cipero si había una actualización o una invalidación de otra CPU pendiente, 140262306a36Sopenharmony_cientonces la especulación será cancelada y el valor recargado: 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci : : +-------+ 140562306a36Sopenharmony_ci +-------+ | | 140662306a36Sopenharmony_ci --->| B->2 |------>| | 140762306a36Sopenharmony_ci +-------+ | CPU 2 | 140862306a36Sopenharmony_ci : :DIVIDE | | 140962306a36Sopenharmony_ci +-------+ | | 141062306a36Sopenharmony_ci La CPU ocupada con la división ---> --->| A->0 |~~~~ | | 141162306a36Sopenharmony_ci especula sobre el LOAD de A +-------+ ~ | | 141262306a36Sopenharmony_ci : : ~ | | 141362306a36Sopenharmony_ci : :DIVIDE | | 141462306a36Sopenharmony_ci : : ~ | | 141562306a36Sopenharmony_ci : : ~ | | 141662306a36Sopenharmony_ci rrrrrrrrrrrrrrrrr | | 141762306a36Sopenharmony_ci +-------+ | | 141862306a36Sopenharmony_ci La especulación es descartada ---> --->| A->1 |------>| | 141962306a36Sopenharmony_ci y un valor actualizado +-------+ | | 142062306a36Sopenharmony_ci es conseguido : : +-------+ 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ciATOMICIDAD MULTICOPIA 142362306a36Sopenharmony_ci--------------------- 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ciLa atomicidad multicopia es una noción profundamente intuitiva sobre el 142662306a36Sopenharmony_ciorden que no es siempre proporcionada por los sistemas informáticos reales, 142762306a36Sopenharmony_cia saber, que un determinada store se vuelve visible al mismo tiempo para 142862306a36Sopenharmony_citodos las CPU o, alternativamente, que todas las CPU acuerdan el orden en 142962306a36Sopenharmony_cique todos los stores se vuelven visibles. Sin embargo, el soporte para 143062306a36Sopenharmony_ciatomicidad multicopia completa descartaría valiosas optimizaciones 143162306a36Sopenharmony_cihardware, por lo que una versión más débil conocida como ``otra atomicidad 143262306a36Sopenharmony_cimulticopia'' en cambio, solo garantiza que un store dado se vuelva visible 143362306a36Sopenharmony_cial mismo tiempo en todas las -otras- CPUs. El resto de este documento 143462306a36Sopenharmony_cidiscute esta versión más débil, pero por brevedad lo llamaremos simplemente 143562306a36Sopenharmony_ci``atomicidad multicopia''. 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ciEl siguiente ejemplo demuestra la atomicidad multicopia: 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci CPU 1 CPU 2 CPU 3 144062306a36Sopenharmony_ci ======================= ======================= ======================= 144162306a36Sopenharmony_ci { X = 0, Y = 0 } 144262306a36Sopenharmony_ci STORE X=1 r1=LOAD X (reads 1) LOAD Y (reads 1) 144362306a36Sopenharmony_ci <barrera general> <barrera de lectura> 144462306a36Sopenharmony_ci STORE Y=r1 LOAD X 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ciSuponga que la carga de la CPU 2 desde X devuelve 1, que luego almacena en 144762306a36Sopenharmony_ciY, y la carga de la CPU 3 desde Y devuelve 1. Esto indica que el store de 144862306a36Sopenharmony_cila CPU 1 a X precede a la carga de la CPU 2 desde X y el store de esa CPU 2 144962306a36Sopenharmony_cia Y precede la carga de la CPU 3 desde Y. Además, las barreras de memoria 145062306a36Sopenharmony_cigarantizan que la CPU 2 ejecuta su carga antes que su almacenamiento, y la 145162306a36Sopenharmony_ciCPU 3 carga desde Y antes de cargar desde X. La pregunta entonces es 145262306a36Sopenharmony_ci"¿Puede la carga de la CPU 3 desde X devolver 0?" 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ciDebido a que la carga de la CPU 3 desde X en cierto sentido viene después 145562306a36Sopenharmony_cide la carga de la CPU 2, es natural esperar que la carga de la CPU 3 desde 145662306a36Sopenharmony_ciX deba devolver 1. Esta expectativa se deriva de la atomicidad multicopia: 145762306a36Sopenharmony_cisi una carga que se ejecuta en la CPU B sigue una carga de la misma 145862306a36Sopenharmony_civariable que se ejecuta en la CPU A (y la CPU A no almacenó originalmente 145962306a36Sopenharmony_ciel valor que leyó), entonces en sistemas atómicos multicopia, la carga de 146062306a36Sopenharmony_cila CPU B debe devolver el mismo valor que hizo la carga de la CPU A o algún 146162306a36Sopenharmony_civalor posterior. Sin embargo, el kernel Linux no requiere que los sistemas 146262306a36Sopenharmony_cisean atómicos multicopia. 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ciEl uso de una barrera de memoria general en el ejemplo anterior compensa 146562306a36Sopenharmony_cicualquier falta de atomicidad multicopia. En el ejemplo, si la carga de la 146662306a36Sopenharmony_ciCPU 2 de X devuelve 1 y la carga de la CPU 3 de Y devuelve 1, entonces la 146762306a36Sopenharmony_cicarga de la CPU 3 desde X debe de hecho también devolver 1. 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ciSin embargo, las dependencias, las barreras de lectura y las barreras de 147062306a36Sopenharmony_ciescritura no siempre son capaces de compensar la atomicidad no multicopia. 147162306a36Sopenharmony_ciPor ejemplo, supongamos que la barrera general de la CPU 2 se elimina del 147262306a36Sopenharmony_ciejemplo anterior, dejando solo la dependencia de datos que se muestra a 147362306a36Sopenharmony_cicontinuación: 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci CPU 1 CPU 2 CPU 3 147662306a36Sopenharmony_ci ======================= ======================= ======================= 147762306a36Sopenharmony_ci { X = 0, Y = 0 } 147862306a36Sopenharmony_ci STORE X=1 r1=LOAD X (escribe 1) LOAD Y (lee 1) 147962306a36Sopenharmony_ci <dependencia de datos> <barrera de lectura> 148062306a36Sopenharmony_ci STORE Y=r1 LOAD X (lee 0) 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ciEsta sustitución permite que la atomicidad no multicopia se desenfrene: en 148362306a36Sopenharmony_cieste ejemplo, es perfectamente legal que la carga de la CPU 2 desde X 148462306a36Sopenharmony_cidevuelva 1, la carga de la CPU 3 desde Y devuelva 1, y su carga desde X 148562306a36Sopenharmony_citenga valor 0. 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ciEl punto clave es que aunque la dependencia de datos de la CPU 2 ordena su 148862306a36Sopenharmony_cicarga y store, no garantiza ordenar el store de la CPU 1. De forma que, si 148962306a36Sopenharmony_cieste ejemplo se ejecuta en un sistema atómico no multicopia donde las CPU 1 149062306a36Sopenharmony_ciy 2 comparten un buffer de almacenamiento o un nivel de caché, la CPU 2 149162306a36Sopenharmony_cipodría tener acceso anticipado de escritura a CPU 1. Por lo tanto, se 149262306a36Sopenharmony_cirequieren barreras generales para garantizar que todas las CPU acurden el 149362306a36Sopenharmony_ciorden combinado de accesos múltiples. 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ciLas barreras generales pueden compensar no solo la atomicidad no 149662306a36Sopenharmony_cimulticopia, pero también pueden generar orden adicional que puede asegurar 149762306a36Sopenharmony_cique -todas- las CPU percibirán el mismo orden de -todas- las operaciones. 149862306a36Sopenharmony_ciPor el contrario, una cadena de parejas de liberación-adquisición no 149962306a36Sopenharmony_ciproporciona este orden adicional, lo que significa que solo se garantiza 150062306a36Sopenharmony_cique las CPU de la cadena estén de acuerdo en el orden combinado de los 150162306a36Sopenharmony_ciaccesos. Por ejemplo, cambiando a código C en deferencia al fantasma de 150262306a36Sopenharmony_ciHerman Hollerith: 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci int u, v, x, y, z; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci void cpu0(void) 150762306a36Sopenharmony_ci { 150862306a36Sopenharmony_ci r0 = smp_load_acquire(&x); 150962306a36Sopenharmony_ci WRITE_ONCE(u, 1); 151062306a36Sopenharmony_ci smp_store_release(&y, 1); 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci void cpu1(void) 151462306a36Sopenharmony_ci { 151562306a36Sopenharmony_ci r1 = smp_load_acquire(&y); 151662306a36Sopenharmony_ci r4 = READ_ONCE(v); 151762306a36Sopenharmony_ci r5 = READ_ONCE(u); 151862306a36Sopenharmony_ci smp_store_release(&z, 1); 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci void cpu2(void) 152262306a36Sopenharmony_ci { 152362306a36Sopenharmony_ci r2 = smp_load_acquire(&z); 152462306a36Sopenharmony_ci smp_store_release(&x, 1); 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci void cpu3(void) 152862306a36Sopenharmony_ci { 152962306a36Sopenharmony_ci WRITE_ONCE(v, 1); 153062306a36Sopenharmony_ci smp_mb(); 153162306a36Sopenharmony_ci r3 = READ_ONCE(u); 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ciDado que cpu0(), cpu1() y cpu2() participan en una cadena de parejas 153562306a36Sopenharmony_cismp_store_release()/smp_load_acquire(), el siguiente resultado estaría 153662306a36Sopenharmony_ciprohibido: 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci r0 == 1 && r1 == 1 && r2 == 1 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ciAdemás, debido a la relación liberación-adquisición entre cpu0() y cpu1(), 154162306a36Sopenharmony_cicpu1() debe ver las escrituras de cpu0(), de modo que el siguiente 154262306a36Sopenharmony_ciresultado estaría prohibido: 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci r1 == 1 && r5 == 0 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ciSin embargo, el orden proporcionado por una cadena de 154762306a36Sopenharmony_ciliberación-adquisición es local a las CPU que participan en esa cadena y no 154862306a36Sopenharmony_cise aplica a cpu3(), al menos aparte de los stores. Por lo tanto, es posible 154962306a36Sopenharmony_ciel siguiente resultado: 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci r0 == 0 && r1 == 1 && r2 == 1 && r3 == 0 && r4 == 0 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ciPor otro lado, también el siguiente resultado es posible: 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci r0 == 0 && r1 == 1 && r2 == 1 && r3 == 0 && r4 == 0 && r5 == 1 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ciAunque cpu0(), cpu1() y cpu2() verán sus respectivas lecturas y escrituras 155862306a36Sopenharmony_cien orden, las CPU que no participan en la cadena de liberación-adquisición 155962306a36Sopenharmony_cipueden estar en desacuerdo con el orden. Este desacuerdo se debe al hecho 156062306a36Sopenharmony_cide que las instrucciones de barrera de memoria débiles utilizadas para 156162306a36Sopenharmony_ciimplementar smp_load_acquire() y smp_store_release() no son necesarios para 156262306a36Sopenharmony_ciordenar stores anteriores contra cargas posteriores en todos los casos. 156362306a36Sopenharmony_ciEsto significa que cpu3() puede ver el store de cpu0() suceder -después- de 156462306a36Sopenharmony_cila carga de cpu1() desde v, aunque tanto cpu0() como cpu1() están de 156562306a36Sopenharmony_ciacuerdo en que estas dos operaciones ocurrieron en el orden previsto. 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ciSin embargo, tenga en cuenta que smp_load_acquire() no es mágico. En 156862306a36Sopenharmony_ciparticular, simplemente lee de su argumento en orden. Es decir, -no- 156962306a36Sopenharmony_ciasegura que se leerá cualquier valor en particular. Por lo tanto, los 157062306a36Sopenharmony_cisiguiente resultados son posibles: 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci r0 == 0 && r1 == 0 && r2 == 0 && r5 == 0 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ciTenga en cuenta que este resultado puede ocurrir incluso en un mítico 157562306a36Sopenharmony_cisistema, consistente en secuencia, donde nunca se reordena nada. 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ciPara reiterar, si su código requiere un orden completo de todas las 157862306a36Sopenharmony_cioperaciones, utilice barreras generales en todo momento. 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci============================== 158262306a36Sopenharmony_ciBARRERAS EXPLÍCITAS DEL KERNEL 158362306a36Sopenharmony_ci============================== 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ciEl kernel Linux tiene una variedad de diferentes barreras que actúan a 158662306a36Sopenharmony_cidiferentes niveles: 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci (*) Barrera del compilador. 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci (*) Barreras de memoria de la CPU. 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ciBARRERA DEL COMPILADOR 159462306a36Sopenharmony_ci----------------------- 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ciEl kernel de Linux tiene una función de barrera del compilador explícita 159762306a36Sopenharmony_cique evita que el el compilador mueva los accesos a la memoria de cualquier 159862306a36Sopenharmony_cilado al otro: 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci barrier(); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ciEsta es una barrera general: no hay variantes de barrier() para casos de 160362306a36Sopenharmony_cilectura-lectura o escritura-escritura. Sin embargo, READ_ONCE() y 160462306a36Sopenharmony_ciWRITE_ONCE() pueden ser considerado como formas débiles de barrier() que 160562306a36Sopenharmony_ciafectan solo específicos accesos marcados por READ_ONCE() o WRITE_ONCE(). 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ciLa función barrier() produce los siguientes efectos: 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci (*) Evita que el compilador reordene los accesos tras barrier() para 161062306a36Sopenharmony_ci preceder a cualquier acceso que preceda a barrier(). Un ejemplo de uso 161162306a36Sopenharmony_ci de esta propiedad es facilitar la comunicación entre código del 161262306a36Sopenharmony_ci interrupt-handler (encargo de gestionar interrupciones) y el código 161362306a36Sopenharmony_ci que fue interrumpido. 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci (*) Dentro de un bucle ("loop"), obliga al compilador a cargar las 161662306a36Sopenharmony_ci variables utilizadas en ese loop condicional en cada paso a través de 161762306a36Sopenharmony_ci ese loop. 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ciLas funciones READ_ONCE() y WRITE_ONCE() pueden evitar cualquier cantidad 162062306a36Sopenharmony_cide optimizaciones que, si bien son perfectamente seguras en código de un 162162306a36Sopenharmony_cisolo subproceso, pueden resultar fatales en código concurrente. Aquí hay 162262306a36Sopenharmony_cialgunos ejemplos de tal tipo de optimizaciones: 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci(*) El compilador está en su derecho de reordenar cargas y stores de la 162562306a36Sopenharmony_ci misma variable, y en algunos casos, la CPU está dentro de su 162662306a36Sopenharmony_ci derecho de reordenar cargas a la misma variable. Esto significa que 162762306a36Sopenharmony_ci el siguiente código: 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci a[0] = x; 163062306a36Sopenharmony_ci a[1] = x; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci Podría resultar en un valor más antiguo de x almacenado en a[1] que en 163362306a36Sopenharmony_ci a[0]. Evite que tanto el compilador como la CPU hagan esto de la 163462306a36Sopenharmony_ci siguiente manera: 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci a[0] = READ_ONCE(x); 163762306a36Sopenharmony_ci a[1] = READ_ONCE(x); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci En resumen, READ_ONCE() y WRITE_ONCE() proporcionan coherencia de 164062306a36Sopenharmony_ci caché para accesos desde múltiples CPUs a una sola variable. 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci (*) El compilador tiene derecho a juntar cargas sucesivas de la misma 164362306a36Sopenharmony_ci variable. Tal fusión puede hacer que el compilador "optimice" el 164462306a36Sopenharmony_ci siguiente código: 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci while (tmp = a) 164762306a36Sopenharmony_ci hacer_algo_con(tmp); 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci en el siguiente código, que, aunque en cierto sentido es legítimo 165062306a36Sopenharmony_ci para un código de un solo subproceso, es casi seguro que no es lo 165162306a36Sopenharmony_ci que el desarrollador pretendía: 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci if (tmp = a) 165462306a36Sopenharmony_ci for (;;) 165562306a36Sopenharmony_ci hacer_algo_con(tmp); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci Use READ_ONCE() para evitar que el compilador le haga esto: 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci while (tmp = READ_ONCE(a)) 166062306a36Sopenharmony_ci hacer_algo_con(tmp); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci (*) El compilador tiene derecho a recargar una variable, por ejemplo, 166362306a36Sopenharmony_ci en los casos en que la alta presión de los registros impida que el 166462306a36Sopenharmony_ci compilador mantenga todos los datos de interés en registros. El 166562306a36Sopenharmony_ci compilador podría por lo tanto, optimizar la variable 'tmp' de nuestro 166662306a36Sopenharmony_ci ejemplo anterior: 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci while (tmp = a) 166962306a36Sopenharmony_ci hacer_algo_con(tmp); 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci Esto podría resultar en el siguiente código, que es perfectamente 167262306a36Sopenharmony_ci seguro en código de subproceso único, pero puede ser fatal en código 167362306a36Sopenharmony_ci concurrente: 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci while (a) 167662306a36Sopenharmony_ci hacer_algo_con(a); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci Por ejemplo, la versión optimizada de este código podría resultar en 167962306a36Sopenharmony_ci pasar un cero a hacer_algo_con() en el caso de que la variable a sea 168062306a36Sopenharmony_ci modificada por alguna otra CPU, entre la instrucción "while" y la 168162306a36Sopenharmony_ci llamada a hacer_algo_con(). 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci De nuevo, use READ_ONCE() para evitar que el compilador haga esto: 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci while (tmp = READ_ONCE(a)) 168662306a36Sopenharmony_ci hacer_algo_con(tmp); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci Tenga en cuenta que si el compilador se queda sin registros, podría 168962306a36Sopenharmony_ci guardar tmp en la pila ("stack"). El overhead (coste en eficiencia) de 169062306a36Sopenharmony_ci este guardado y posterior restauración es por lo que los compiladores 169162306a36Sopenharmony_ci recargan las variables. Hacerlo es perfectamente seguro para código de 169262306a36Sopenharmony_ci subproceso único, por lo que debe informar al compilador sobre los 169362306a36Sopenharmony_ci casos donde no sea seguro. 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci (*) El compilador está en su derecho de omitir una carga por completo si 169662306a36Sopenharmony_ci sabe cual será su valor. Por ejemplo, si el compilador puede probar 169762306a36Sopenharmony_ci que el valor de la variable 'a' siempre es cero, puede optimizar este 169862306a36Sopenharmony_ci código: 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci while (tmp = a) 170162306a36Sopenharmony_ci hacer_algo_con(tmp); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci En esto: 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci do { } while (0); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci Esta transformación es una victoria para un código de un solo 170862306a36Sopenharmony_ci subproceso, porque se deshace de una carga y un branch. El problema es 170962306a36Sopenharmony_ci que el compilador llevará a cabo su prueba asumiendo que la CPU actual 171062306a36Sopenharmony_ci es la única actualizando la variable 'a'. Si la variable 'a' es 171162306a36Sopenharmony_ci compartida, entonces la prueba del compilador será errónea. Use 171262306a36Sopenharmony_ci READ_ONCE() para decirle al compilador que no sabe tanto como cree: 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci while (tmp = READ_ONCE(a)) 171562306a36Sopenharmony_ci hacer_algo_con(tmp); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci Pero, por favor, tenga en cuenta que el compilador también está 171862306a36Sopenharmony_ci observando de cerca lo que usted hace con el valor después de 171962306a36Sopenharmony_ci READ_ONCE(). Por ejemplo, suponga que Ud. hace lo siguiente y MAX es 172062306a36Sopenharmony_ci una macro de preprocesador con el valor 1: 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci while ((tmp = READ_ONCE(a)) % MAX) 172362306a36Sopenharmony_ci hacer_algo_con(tmp); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci Entonces el compilador sabe que el resultado del operador "%" aplicado 172662306a36Sopenharmony_ci a MAX siempre será cero, nuevamente permitiendo que el compilador 172762306a36Sopenharmony_ci optimice el código hasta su casi inexistencia. (Aún se cargará desde 172862306a36Sopenharmony_ci la variable 'a'.) 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci (*) De manera similar, el compilador tiene derecho a omitir un store por 173162306a36Sopenharmony_ci completo si sabe que la variable ya tiene el valor almacenado. 173262306a36Sopenharmony_ci Nuevamente, el compilador asume que la CPU actual es la única que 173362306a36Sopenharmony_ci almacena la variable, lo que puede hacer que el compilador haga 173462306a36Sopenharmony_ci algo incorrecto para las variables compartidas. Por ejemplo, suponga 173562306a36Sopenharmony_ci que tiene lo siguiente: 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci a = 0; 173862306a36Sopenharmony_ci ... Código que no almacena la variable a ... 173962306a36Sopenharmony_ci a = 0; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci El compilador observa que el valor de la variable 'a' ya es cero, por 174262306a36Sopenharmony_ci lo que bien podría omitir el segundo store. Esto supondría una fatal 174362306a36Sopenharmony_ci sorpresa, si alguna otra CPU hubiera almacenado la variable 'a' 174462306a36Sopenharmony_ci mientras tanto. 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci Use WRITE_ONCE() para evitar que el compilador haga este tipo de 174762306a36Sopenharmony_ci suposición equivocada: 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci WRITE_ONCE(a, 0); 175062306a36Sopenharmony_ci ... Código que no almacena la variable a ... 175162306a36Sopenharmony_ci WRITE_ONCE(a, 0); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci (*) El compilador tiene derecho a reordenar los accesos a memoria a menos 175462306a36Sopenharmony_ci que le diga que no. Por ejemplo, considere la siguiente interacción 175562306a36Sopenharmony_ci entre el código de nivel de proceso y un controlador de interrupción: 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci void nivel_de_procesamiento(void) 175862306a36Sopenharmony_ci { 175962306a36Sopenharmony_ci msg = ACQUIRE_mensaje(); 176062306a36Sopenharmony_ci flag = true; 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci void controlador_interrupcion(void) 176462306a36Sopenharmony_ci { 176562306a36Sopenharmony_ci if (flag) 176662306a36Sopenharmony_ci procesar_mensaje(msg); 176762306a36Sopenharmony_ci } 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci No hay nada que impida que el compilador transforme 177062306a36Sopenharmony_ci nivel_de_procesamiento() a lo siguiente, que de hecho, bien podría ser 177162306a36Sopenharmony_ci una victoria para código de un solo subproceso: 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci void nivel_de_procesamiento(void) 177462306a36Sopenharmony_ci { 177562306a36Sopenharmony_ci flag = true; 177662306a36Sopenharmony_ci msg = ACQUIRE_mensaje(); 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci Si la interrupción ocurre entre estas dos declaraciones, entonces 178062306a36Sopenharmony_ci controlador_interrupcion() podría recibir un mensaje ilegible. Use 178162306a36Sopenharmony_ci READ_ONCE() para evitar esto de la siguiente manera: 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci void nivel_de_procesamiento(void) 178462306a36Sopenharmony_ci { 178562306a36Sopenharmony_ci WRITE_ONCE(msg, ACQUIRE_mensaje()); 178662306a36Sopenharmony_ci WRITE_ONCE(flag, true); 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci void controlador_interrupcion(void) 179062306a36Sopenharmony_ci { 179162306a36Sopenharmony_ci if (READ_ONCE(flag)) 179262306a36Sopenharmony_ci procesar_mensaje(READ_ONCE(msg)); 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci Tenga en cuenta que los envoltorios ("wrappers") READ_ONCE() y 179662306a36Sopenharmony_ci WRITE_ONCE() en controlador_interrupcion() son necesarios si este 179762306a36Sopenharmony_ci controlador de interrupciones puede ser interrumpido por algo que 179862306a36Sopenharmony_ci también accede a 'flag' y 'msg', por ejemplo, una interrupción anidada 179962306a36Sopenharmony_ci o un NMI. De lo contrario, READ_ONCE() y WRITE_ONCE() no son 180062306a36Sopenharmony_ci necesarios en controlador_interrupcion() aparte de con fines de 180162306a36Sopenharmony_ci documentación. (Tenga también en cuenta que las interrupciones 180262306a36Sopenharmony_ci anidadas no ocurren típicamente en los kernels Linux modernos, de 180362306a36Sopenharmony_ci hecho, si un controlador de interrupciones regresa con interrupciones 180462306a36Sopenharmony_ci habilitadas, obtendrá un WARN_ONCE().) 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci Debe suponer que el compilador puede mover READ_ONCE() y WRITE_ONCE() 180762306a36Sopenharmony_ci a código que no contiene READ_ONCE(), WRITE_ONCE(), barrier(), o 180862306a36Sopenharmony_ci primitivas similares. 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci Este efecto también podría lograrse usando barrier(), pero READ_ONCE() 181162306a36Sopenharmony_ci y WRITE_ONCE() son más selectivos: Con READ_ONCE() y WRITE_ONCE(), el 181262306a36Sopenharmony_ci compilador solo necesita olvidar el contenido de ubicaciones de 181362306a36Sopenharmony_ci memoria indicadas, mientras que con barrier() el compilador debe 181462306a36Sopenharmony_ci descartar el valor de todas las ubicaciones de memoria que tiene 181562306a36Sopenharmony_ci actualmente almacenadas en caché, en cualquier registro de la máquina. 181662306a36Sopenharmony_ci Por supuesto, el compilador también debe respetar el orden en que 181762306a36Sopenharmony_ci ocurren READ_ONCE() y WRITE_ONCE(), aunque la CPU, efectivamente, no 181862306a36Sopenharmony_ci necesita hacerlo. 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci (*) El compilador tiene derecho a inventar stores para una variable, 182162306a36Sopenharmony_ci como en el siguiente ejemplo: 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (a) 182462306a36Sopenharmony_ci b = a; 182562306a36Sopenharmony_ci else 182662306a36Sopenharmony_ci b = 42; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci El compilador podría ahorrar un branch al optimizar esto de la 182962306a36Sopenharmony_ci siguiente manera: 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci b = 42; 183262306a36Sopenharmony_ci if (a) 183362306a36Sopenharmony_ci b = a; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci En el código de un solo subproceso, esto no solo es seguro, sino que 183662306a36Sopenharmony_ci también ahorra un branch. Desafortunadamente, en código concurrente, 183762306a36Sopenharmony_ci esta optimización podría causar que alguna otra CPU vea un valor falso 183862306a36Sopenharmony_ci de 42, incluso si la variable 'a' nunca fue cero, al cargar la 183962306a36Sopenharmony_ci variable 'b'. Use WRITE_ONCE() para evitar esto de la siguiente 184062306a36Sopenharmony_ci manera: 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci if (a) 184362306a36Sopenharmony_ci WRITE_ONCE(b, a); 184462306a36Sopenharmony_ci else 184562306a36Sopenharmony_ci WRITE_ONCE(b, 42); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci El compilador también puede inventar cargas. Estos casos suelen ser 184862306a36Sopenharmony_ci menos perjudiciales, pero pueden dar como resultado "bouncing" de la 184962306a36Sopenharmony_ci línea de caché y, por lo tanto, bajo rendimiento y escalabilidad. 185062306a36Sopenharmony_ci Utilice READ_ONCE() para evitar cargas inventadas. 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci (*) Para ubicaciones de memoria alineadas cuyo tamaño les permita 185362306a36Sopenharmony_ci acceder con una sola instrucción de referencia de memoria, evite el 185462306a36Sopenharmony_ci "desgarro de la carga" (load tearing) y "desgarro del store" (store 185562306a36Sopenharmony_ci tearing), en el que un solo gran acceso es reemplazado por múltiples 185662306a36Sopenharmony_ci accesos menores. Por ejemplo, dada una arquitectura que tiene 185762306a36Sopenharmony_ci instrucciones de almacenamiento de 16 bits con campos inmediatos de 7 185862306a36Sopenharmony_ci bits, el compilador podría tener la tentación de usar dos 185962306a36Sopenharmony_ci instrucciones inmediatas de almacenamiento de 16 bits para implementar 186062306a36Sopenharmony_ci el siguiente store de 32 bits: 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci p = 0x00010002; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci Tenga en cuenta que GCC realmente usa este tipo de optimización, lo 186562306a36Sopenharmony_ci cual no es sorprendente dado que probablemente costaría más de dos 186662306a36Sopenharmony_ci instrucciones el construir la constante y luego almacenarla. Por lo 186762306a36Sopenharmony_ci tanto, esta optimización puede ser una victoria en un código de un 186862306a36Sopenharmony_ci solo subproceso. De hecho, un error reciente (desde que se solucionó) 186962306a36Sopenharmony_ci hizo que GCC usara incorrectamente esta optimización en un store 187062306a36Sopenharmony_ci volátil. En ausencia de tales errores, el uso de WRITE_ONCE() evita el 187162306a36Sopenharmony_ci desgarro del store en el siguiente ejemplo: 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci struct __attribute__((__packed__)) foo { 187462306a36Sopenharmony_ci short a; 187562306a36Sopenharmony_ci int b; 187662306a36Sopenharmony_ci short c; 187762306a36Sopenharmony_ci }; 187862306a36Sopenharmony_ci struct foo foo1, foo2; 187962306a36Sopenharmony_ci ... 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci foo2.a = foo1.a; 188262306a36Sopenharmony_ci foo2.b = foo1.b; 188362306a36Sopenharmony_ci foo2.c = foo1.c; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci Debido a que no hay envoltorios READ_ONCE() o WRITE_ONCE() y no 188662306a36Sopenharmony_ci hay markings volátiles, el compilador estaría en su derecho de 188762306a36Sopenharmony_ci implementar estas tres declaraciones de asignación como un par de 188862306a36Sopenharmony_ci cargas de 32 bits, seguido de un par de stores de 32 bits. Esto 188962306a36Sopenharmony_ci resultaría en una carga con desgarro en 'foo1.b' y store del desgarro 189062306a36Sopenharmony_ci en 'foo2.b'. READ_ONCE() y WRITE_ONCE() nuevamente evitan el desgarro 189162306a36Sopenharmony_ci en este ejemplo: 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci foo2.a = foo1.a; 189462306a36Sopenharmony_ci WRITE_ONCE(foo2.b, READ_ONCE(foo1.b)); 189562306a36Sopenharmony_ci foo2.c = foo1.c; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ciAparte de esto, nunca es necesario usar READ_ONCE() y WRITE_ONCE() en una 189862306a36Sopenharmony_civariable que se ha marcado como volátil. Por ejemplo, dado que 'jiffies' 189962306a36Sopenharmony_ciestá marcado como volátil, nunca es necesario usar READ_ONCE(jiffies). La 190062306a36Sopenharmony_cirazón de esto es que READ_ONCE() y WRITE_ONCE() se implementan como 190162306a36Sopenharmony_ciconversiones volátiles, lo que no tiene efecto cuando su argumento ya está 190262306a36Sopenharmony_cimarcado como volátil. 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ciTenga en cuenta que estas barreras del compilador no tienen un efecto 190562306a36Sopenharmony_cidirecto en la CPU, que luego puede reordenar las cosas como quiera. 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ciBARRERAS DE MEMORIA DE LA CPU 190962306a36Sopenharmony_ci----------------------------- 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ciEl kernel de Linux tiene siete barreras básicas de memoria de CPU: 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ciTIPO OBLIGATORIO SMP CONDICIONAL 191462306a36Sopenharmony_ci======================= =============== =============== 191562306a36Sopenharmony_ciGENERAL mb() smp_mb() 191662306a36Sopenharmony_ciWRITE wmb() smp_wmb() 191762306a36Sopenharmony_ciREAD rmb() smp_rmb() 191862306a36Sopenharmony_ciDEPEDENCIA DE DIRECCIÓN READ_ONCE() 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ciTodas las barreras de memoria, excepto las barreras de dependencia de 192262306a36Sopenharmony_cidirecciones, implican una barrera del compilador. Las dependencias de 192362306a36Sopenharmony_cidirecciones no imponen ningún orden de compilación adicional. 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ciAdemás: en el caso de las dependencias de direcciones, se esperaría que el 192662306a36Sopenharmony_cicompilador emita las cargas en el orden correcto (por ejemplo, `a[b]` 192762306a36Sopenharmony_citendría que cargar el valor de b antes de cargar a[b]), sin embargo, no hay 192862306a36Sopenharmony_cigarantía alguna en la especificación de C sobre que el compilador no puede 192962306a36Sopenharmony_ciespecular el valor de b (por ejemplo, es igual a 1) y carga a[b] antes que 193062306a36Sopenharmony_cib (ej. tmp = a[1]; if (b != 1) tmp = a[b]; ). También existe el problema de 193162306a36Sopenharmony_cique un compilador vuelva a cargar b después de haber cargado a[b], teniendo 193262306a36Sopenharmony_ciasí una copia más nueva de b que a[b]. Aún no se ha conseguido un consenso 193362306a36Sopenharmony_ciacerca de estos problemas, sin embargo, el macro READ_ONCE() es un buen 193462306a36Sopenharmony_cilugar para empezar a buscar. 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ciLas barreras de memoria SMP se reducen a barreras de compilador cuando se 193762306a36Sopenharmony_cicompila a monoprocesador, porque se supone que una CPU parecerá ser 193862306a36Sopenharmony_ciauto-consistente, y ordenará correctamente los accesos superpuestos 193962306a36Sopenharmony_cirespecto a sí misma. Sin embargo, consulte la subsección "Guests de 194062306a36Sopenharmony_cimáquinas virtuales" mas adelante. 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci[!] Tenga en cuenta que las barreras de memoria SMP _deben_ usarse para 194362306a36Sopenharmony_cicontrolar el orden de referencias a memoria compartida en sistemas SMP, 194462306a36Sopenharmony_ciaunque el uso de bloqueo en su lugar sea suficiente. 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ciLas barreras obligatorias no deben usarse para controlar los efectos de 194762306a36Sopenharmony_ciSMP, ya que dichas barreras imponen una sobrecarga innecesaria en los 194862306a36Sopenharmony_cisistemas SMP y UP. Se pueden, sin embargo, usar para controlar los efectos 194962306a36Sopenharmony_ciMMIO en los accesos a través de ventanas E/S de memoria relajada. Estas 195062306a36Sopenharmony_cibarreras son necesarias incluso en sistemas que no son SMP, ya que afectan 195162306a36Sopenharmony_cial orden en que las operaciones de memoria aparecen en un dispositivo, al 195262306a36Sopenharmony_ciprohibir tanto al compilador como a la CPU que sean reordenados. 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ciHay algunas funciones de barrera más avanzadas: 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci (*) smp_store_mb(var, valor) 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci Asigna el valor a la variable y luego inserta una barrera de memoria 196062306a36Sopenharmony_ci completa después de ella. No se garantiza insertar nada más que una 196162306a36Sopenharmony_ci barrera del compilador en una compilación UP. 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci (*) smp_mb__before_atomic(); 196562306a36Sopenharmony_ci (*) smp_mb__after_atomic(); 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci Estos se pueden usar con funciones RMW atómicas que no implican 196862306a36Sopenharmony_ci barreras de memoria, pero donde el código necesita una barrera de 196962306a36Sopenharmony_ci memoria. Ejemplos de funciones RMW atómicas que no implican una 197062306a36Sopenharmony_ci barrera de memoria son, por ejemplo, agregar, restar, operaciones 197162306a36Sopenharmony_ci condicionales (fallidas), funciones _relaxed, pero no atomic_read o 197262306a36Sopenharmony_ci atomic_set. Un ejemplo común donde se puede requerir una barrera es 197362306a36Sopenharmony_ci cuando se usan operaciones atómicas como referencia de contador. 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci Estos también se utilizan para funciones atómicas RMW bitop que no 197662306a36Sopenharmony_ci implican una barrera de memoria (como set_bit y clear_bit). 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci Como ejemplo, considere una pieza de código que marca un objeto como 197962306a36Sopenharmony_ci muerto y luego disminuye el contador de referencias del objeto: 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci obj->dead = 1; 198262306a36Sopenharmony_ci smp_mb__before_atomic(); 198362306a36Sopenharmony_ci atomic_dec(&obj->ref_count); 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci Esto asegura que la marca de muerte en el objeto se perciba como 198662306a36Sopenharmony_ci fijada *antes* de que disminuya el contador de referencia. 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci Consulte Documentation/atomic_{t,bitops}.txt para obtener más 198962306a36Sopenharmony_ci información. 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci (*) dma_wmb(); 199362306a36Sopenharmony_ci (*) dma_rmb(); 199462306a36Sopenharmony_ci (*) dma_mb(); 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci Estos son usados con memoria consistente para garantizar el orden de 199762306a36Sopenharmony_ci escrituras o lecturas de memoria compartida accesible tanto para la 199862306a36Sopenharmony_ci CPU como para un dispositivo compatible con DMA. 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci Por ejemplo, considere un controlador de dispositivo que comparte 200162306a36Sopenharmony_ci memoria con otro dispositivo y usa un valor de estado del descriptor 200262306a36Sopenharmony_ci para indicar si el descriptor pertenece al dispositivo o a la CPU, y 200362306a36Sopenharmony_ci un "doorbell" (timbre, punto de acceso) para avisarle cuando haya 200462306a36Sopenharmony_ci nuevos descriptores disponibles: 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci if (desc->status != DEVICE_OWN) { 200762306a36Sopenharmony_ci /* no leer los datos hasta que tengamos el descriptor */ 200862306a36Sopenharmony_ci dma_rmb(); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci /* leer/modificar datos */ 201162306a36Sopenharmony_ci read_data = desc->data; 201262306a36Sopenharmony_ci desc->data = write_data; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci /* flush de modificaciones antes de la actualización de estado */ 201562306a36Sopenharmony_ci dma_wmb(); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* asignar propiedad */ 201862306a36Sopenharmony_ci desc->status = DEVICE_OWN; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* notificar al dispositivo de nuevos descriptores */ 202162306a36Sopenharmony_ci writel(DESC_NOTIFY, doorbell); 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci El dma_rmb() nos permite garantizar que el dispositivo ha liberado su 202562306a36Sopenharmony_ci propiedad antes de que leamos los datos del descriptor, y el dma_wmb() 202662306a36Sopenharmony_ci permite garantizar que los datos se escriben en el descriptor antes de 202762306a36Sopenharmony_ci que el dispositivo pueda ver que ahora tiene la propiedad. El dma_mb() 202862306a36Sopenharmony_ci implica tanto un dma_rmb() como un dma_wmb(). Tenga en cuenta que, al 202962306a36Sopenharmony_ci usar writel(), no se necesita un wmb() anterior para garantizar que 203062306a36Sopenharmony_ci las escrituras de la memoria caché coherente se hayan completado antes 203162306a36Sopenharmony_ci escribiendo a la región MMIO. El writel_relaxed() más barato no 203262306a36Sopenharmony_ci proporciona esta garantía y no debe utilizarse aquí. 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci Consulte la subsección "Efectos de barrera de E/S del kernel" para 203562306a36Sopenharmony_ci obtener más información sobre accesorios de E/S relajados y el archivo 203662306a36Sopenharmony_ci Documentation/core-api/dma-api.rst para más información sobre memoria 203762306a36Sopenharmony_ci consistente. 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci (*) pmem_wmb(); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci Es es para uso con memoria persistente para garantizar que los stores 204262306a36Sopenharmony_ci para los que las modificaciones se escriben en el almacenamiento 204362306a36Sopenharmony_ci persistente llegaron a dominio de durabilidad de la plataforma. 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci Por ejemplo, después de una escritura no temporal en la región pmem, 204662306a36Sopenharmony_ci usamos pmem_wmb() para garantizar que los stores hayan alcanzado el 204762306a36Sopenharmony_ci dominio de durabilidad de la plataforma. Esto garantiza que los stores 204862306a36Sopenharmony_ci han actualizado el almacenamiento persistente antes de cualquier 204962306a36Sopenharmony_ci acceso a datos o transferencia de datos causada por instrucciones 205062306a36Sopenharmony_ci posteriores. Esto es además del orden realizado por wmb(). 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci Para la carga desde memoria persistente, las barreras de memoria de 205362306a36Sopenharmony_ci lectura existentes son suficientes para garantizar el orden de 205462306a36Sopenharmony_ci lectura. 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci (*) io_stop_wc(); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci Para accesos a memoria con atributos de combinación de escritura (por 205962306a36Sopenharmony_ci ejemplo, los devueltos por ioremap_wc(), la CPU puede esperar a que 206062306a36Sopenharmony_ci los accesos anteriores se junten con posteriores. io_stop_wc() se 206162306a36Sopenharmony_ci puede utilizar para evitar la combinación de accesos a memoria de 206262306a36Sopenharmony_ci de escritura antes de esta macro, con los posteriores, cuando dicha 206362306a36Sopenharmony_ci espera tenga implicaciones en el rendimiento. 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci========================================= 206662306a36Sopenharmony_ciBARRERAS DE MEMORIA IMPLÍCITAS DEL KERNEL 206762306a36Sopenharmony_ci========================================= 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ciAlgunas de las otras funciones en el kernel Linux implican barreras de 207062306a36Sopenharmony_cimemoria, entre estas encontramos funciones de bloqueo y planificación 207162306a36Sopenharmony_ci("scheduling"). 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ciEsta especificación es una garantía _mínima_; cualquier arquitectura 207462306a36Sopenharmony_ciparticular puede proporcionar garantías más sustanciales, pero no se puede 207562306a36Sopenharmony_ciconfiar en estas fuera de código específico de arquitectura. 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ciFUNCIONES DE ADQUISICIÓN DE CERROJO 207962306a36Sopenharmony_ci----------------------------------- 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ciEl kernel Linux tiene una serie de abstracciones de bloqueo: 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci (*) spin locks (cerrojos en loop) 208462306a36Sopenharmony_ci (*) R/W spin lock (cerrojos de escritura/lectura) 208562306a36Sopenharmony_ci (*) mutex 208662306a36Sopenharmony_ci (*) semáforos 208762306a36Sopenharmony_ci (*) R/W semáforos 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ciEn todos los casos existen variantes de las operaciones "ACQUIRE" y 209062306a36Sopenharmony_ci"RELEASE" para cada uno de ellos. Todas estas operaciones implican ciertas 209162306a36Sopenharmony_cibarreras: 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci (1) Implicaciones de la operación ACQUIRE: 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci Las operaciones de memoria emitidas después del ACQUIRE se completarán 209662306a36Sopenharmony_ci después de que la operación ACQUIRE haya finalizado. 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci Las operaciones de memoria emitidas antes de ACQUIRE pueden 209962306a36Sopenharmony_ci completarse después que la operación ACQUIRE se ha completado. 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci (2) Implicaciones de la operación RELEASE: 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci Las operaciones de memoria emitidas antes de la RELEASE se 210462306a36Sopenharmony_ci completarán antes de que la operación de RELEASE se haya completado. 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci Las operaciones de memoria emitidas después de la RELEASE pueden 210762306a36Sopenharmony_ci completarse antes de que la operación de RELEASE se haya completado. 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci (3) Implicación de ACQUIRE vs ACQUIRE: 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci Todas las operaciones ACQUIRE emitidas antes de otra operación 211262306a36Sopenharmony_ci ACQUIRE serán completadas antes de esa operación ACQUIRE. 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci (4) Implicación de ACQUIRE vs RELEASE: 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci Todas las operaciones ACQUIRE emitidas antes de una operación RELEASE 211762306a36Sopenharmony_ci serán completadas antes de la operación RELEASE. 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci (5) Implicación de ACQUIRE condicional fallido: 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci Ciertas variantes de bloqueo de la operación ACQUIRE pueden fallar, ya 212262306a36Sopenharmony_ci sea debido a no poder obtener el bloqueo de inmediato, o debido a que 212362306a36Sopenharmony_ci recibieron una señal de desbloqueo mientras dormían esperando que el 212462306a36Sopenharmony_ci cerrojo estuviera disponible. Los fallos en cerrojos no implican 212562306a36Sopenharmony_ci ningún tipo de barrera. 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci[!] Nota: una de las consecuencias de que los cerrojos en ACQUIRE y RELEASE 212862306a36Sopenharmony_cisean barreras unidireccionales, es que los efectos de las instrucciones 212962306a36Sopenharmony_cifuera de una sección crítica pueden filtrarse al interior de la sección 213062306a36Sopenharmony_cicrítica. 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ciNo se puede suponer que un ACQUIRE seguido de una RELEASE sea una barrera 213362306a36Sopenharmony_cide memoria completa dado que es posible que un acceso anterior a ACQUIRE 213462306a36Sopenharmony_cisuceda después del ACQUIRE, y un acceso posterior a la RELEASE suceda antes 213562306a36Sopenharmony_cidel RELEASE, y los dos accesos puedan entonces cruzarse: 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci *A = a; 213862306a36Sopenharmony_ci ACQUIRE M 213962306a36Sopenharmony_ci RELEASE M 214062306a36Sopenharmony_ci *B = b; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_cipuede ocurrir como: 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci ACQUIRE M, STORE *B, STORE *A, RELEASE M 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ciCuando ACQUIRE y RELEASE son bloqueo de adquisición y liberación, 214762306a36Sopenharmony_cirespectivamente, este mismo orden puede ocurrir si el cerrojo ACQUIRE y 214862306a36Sopenharmony_ciRELEASE son para la misma variable de bloqueo, pero solo desde la 214962306a36Sopenharmony_ciperspectiva de otra CPU que no tiene ese bloqueo. En resumen, un ACQUIRE 215062306a36Sopenharmony_ciseguido de un RELEASE NO puede entenderse como una barrera de memoria 215162306a36Sopenharmony_cicompleta. 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ciDe manera similar, el caso inverso de un RELEASE seguido de un ACQUIRE no 215462306a36Sopenharmony_ciimplica una barrera de memoria completa. Por lo tanto, la ejecución de la 215562306a36Sopenharmony_ciCPU de los tramos críticos correspondientes a la RELEASE y la ACQUIRE 215662306a36Sopenharmony_cipueden cruzarse, de modo que: 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci *A = a; 215962306a36Sopenharmony_ci RELEASE M 216062306a36Sopenharmony_ci ACQUIRE N 216162306a36Sopenharmony_ci *B = b; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cipuede ocurrir como: 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci ACQUIRE N, STORE *B, STORE *A, RELEASE M 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ciPodría parecer que este nuevo orden podría introducir un punto muerto. 216862306a36Sopenharmony_ciSin embargo, esto no puede suceder porque si tal punto muerto amenazara 216962306a36Sopenharmony_cicon suceder, el RELEASE simplemente se completaría, evitando así el 217062306a36Sopenharmony_ciinterbloqueo ("deadlock", punto muerto). 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci ¿Por qué funciona esto? 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci Un punto clave es que solo estamos hablando de la CPU re-haciendo el 217562306a36Sopenharmony_ci orden, no el compilador. Si el compilador (o, ya puestos, el 217662306a36Sopenharmony_ci desarrollador) cambió las operaciones, un deadlock -podría- ocurrir. 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci Pero supongamos que la CPU reordenó las operaciones. En este caso, el 217962306a36Sopenharmony_ci desbloqueo precede al bloqueo en el código ensamblador. La CPU 218062306a36Sopenharmony_ci simplemente eligió intentar ejecutar primero la última operación de 218162306a36Sopenharmony_ci bloqueo. Si hay un interbloqueo, esta operación de bloqueo simplemente 218262306a36Sopenharmony_ci esperará (o tratará de dormir, pero hablaremos de eso más adelante). La 218362306a36Sopenharmony_ci CPU eventualmente ejecutará la operación de desbloqueo (que precedió a la 218462306a36Sopenharmony_ci operación de bloqueo en el código ensamblador), lo que desenmascará el 218562306a36Sopenharmony_ci potencial punto muerto, permitiendo que la operación de bloqueo tenga 218662306a36Sopenharmony_ci éxito. 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci Pero, ¿y si el cerrojo es un cerrojo que duerme ("sleeplock")? En tal 218962306a36Sopenharmony_ci caso, el código intentará entrar al scheduler, donde eventualmente 219062306a36Sopenharmony_ci encontrará una barrera de memoria, que forzará la operación de desbloqueo 219162306a36Sopenharmony_ci anterior para completar, nuevamente desentrañando el punto muerto. Podría 219262306a36Sopenharmony_ci haber una carrera de desbloqueo del sueño ("sleep-unlock race"), pero la 219362306a36Sopenharmony_ci primitiva de bloqueo necesita resolver tales carreras correctamente en 219462306a36Sopenharmony_ci cualquier caso. 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ciEs posible que los cerrojos y los semáforos no proporcionen ninguna 219762306a36Sopenharmony_cigarantía de orden en sistemas compilados en UP, por lo que no se puede 219862306a36Sopenharmony_cicontar con tal situación para lograr realmente nada en absoluto, 219962306a36Sopenharmony_ciespecialmente con respecto a los accesos de E/S, a menos que se combinen 220062306a36Sopenharmony_cicon operaciones de inhabilitación de interrupciones. 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ciConsulte también la sección "Efectos de barrera adquiriendo intra-CPU". 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ciComo ejemplo, considere lo siguiente: 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci *A = a; 220862306a36Sopenharmony_ci *B = b; 220962306a36Sopenharmony_ci ACQUIRE 221062306a36Sopenharmony_ci *C = c; 221162306a36Sopenharmony_ci *D = d; 221262306a36Sopenharmony_ci RELEASE 221362306a36Sopenharmony_ci *E = e; 221462306a36Sopenharmony_ci *F = f; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ciLa siguiente secuencia de eventos es aceptable: 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci ACQUIRE, {*F,*A}, *E, {*C,*D}, *B, RELEASE 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci [+] Tenga en cuenta que {*F,*A} indica un acceso combinado. 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ciPero ninguno de los siguientes lo son: 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci {*F,*A}, *B, ACQUIRE, *C, *D, RELEASE, *E 222562306a36Sopenharmony_ci *A, *B, *C, ACQUIRE, *D, RELEASE, *E, *F 222662306a36Sopenharmony_ci *A, *B, ACQUIRE, *C, RELEASE, *D, *E, *F 222762306a36Sopenharmony_ci *B, ACQUIRE, *C, *D, RELEASE, {*F,*A}, *E 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ciFUNCIONES DE DESACTIVACIÓN DE INTERRUPCIONES 223262306a36Sopenharmony_ci-------------------------------------------- 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ciLas funciones que deshabilitan interrupciones (equivalentes a ACQUIRE) y 223562306a36Sopenharmony_cihabilitan interrupciones (equivalentes a RELEASE) actuarán únicamente como 223662306a36Sopenharmony_cibarrera del compilador. Por consiguiente, si la memoria o la E/S requieren 223762306a36Sopenharmony_cibarreras en tal situación, deben ser provistas por algún otro medio. 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ciFUNCIONES DE DORMIR Y DESPERTAR 224162306a36Sopenharmony_ci------------------------------- 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ciDormir y despertar son eventos marcados ("flagged") en los datos globales 224462306a36Sopenharmony_cique se pueden ver como una interacción entre dos piezas de datos: el estado 224562306a36Sopenharmony_cide la task (hilo, proceso, tarea) que espera el evento y los datos globales 224662306a36Sopenharmony_ciutilizados para indicar el evento. Para asegurarse de que estos parezcan 224762306a36Sopenharmony_cisuceder en el orden correcto, las primitivas para comenzar el proceso de ir 224862306a36Sopenharmony_cia dormir, y las primitivas para iniciar un despertar implican ciertas 224962306a36Sopenharmony_cibarreras. 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ciEn primer lugar, el agente durmiente normalmente sigue algo similar a esta 225262306a36Sopenharmony_cisecuencia de eventos: 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci for (;;) { 225562306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 225662306a36Sopenharmony_ci if (evento_indicado) 225762306a36Sopenharmony_ci break; 225862306a36Sopenharmony_ci schedule(); // planificar 225962306a36Sopenharmony_ci } 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ciUna barrera de memoria general se obtiene automáticamente mediante 226262306a36Sopenharmony_ciset_current_state() después de haber alterado el estado de la tarea: 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci CPU 1 226562306a36Sopenharmony_ci =============================== 226662306a36Sopenharmony_ci set_current_state(); // hacer_estado_actual() 226762306a36Sopenharmony_ci smp_store_mb(); 226862306a36Sopenharmony_ci STORE current->state 226962306a36Sopenharmony_ci <barrera general> 227062306a36Sopenharmony_ci LOAD evento_indicado 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ciset_current_state() puede estar envuelto por: 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci prepare_to_wait(); // preparese_para_esperar(); 227562306a36Sopenharmony_ci prepare_to_wait_exclusive(); // prepararse_para_solo_esperar(); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_cique por lo tanto también implican una barrera de memoria general después de 227862306a36Sopenharmony_ciestablecer el estado. Toda la secuencia anterior está disponible en varias 227962306a36Sopenharmony_ciformas, todas las cuales obtienen la barrera de memoria en el lugar 228062306a36Sopenharmony_cicorrecto: 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci wait_event(); 228362306a36Sopenharmony_ci wait_event_interruptible(); 228462306a36Sopenharmony_ci wait_event_interruptible_exclusive(); 228562306a36Sopenharmony_ci wait_event_interruptible_timeout(); 228662306a36Sopenharmony_ci wait_event_killable(); 228762306a36Sopenharmony_ci wait_event_timeout(); 228862306a36Sopenharmony_ci wait_on_bit(); 228962306a36Sopenharmony_ci wait_on_bit_lock(); 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ciEn segundo lugar, el código que realiza una activación normalmente se 229362306a36Sopenharmony_ciasemeja a algo como esto: 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci evento_indicado = 1; 229662306a36Sopenharmony_ci wake_up(&event_wait_queue); // despertar 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_cio: 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci evento_indicado = 1; 230162306a36Sopenharmony_ci wake_up_process(event_daemon); // despertar proceso 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ciwake_up() ejecuta una barrera de memoria general si despierta algo. Si no 230462306a36Sopenharmony_cidespierta nada, entonces una barrera de memoria puede o no ser ejecutada; 230562306a36Sopenharmony_cino debe confiar en ello. La barrera se produce antes del acceso al estado 230662306a36Sopenharmony_cide la tarea. En particular, se encuentra entre el STORE para indicar el 230762306a36Sopenharmony_cievento y el STORE para configurar TASK_RUNNING (hilo ejecutando): 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci CPU 1 (Durmiente) CPU 2 (Despertadora) 231062306a36Sopenharmony_ci =============================== =============================== 231162306a36Sopenharmony_ci set_current_state(); STORE evento_indicado 231262306a36Sopenharmony_ci smp_store_mb(); wake_up(); 231362306a36Sopenharmony_ci STORE current->state ... 231462306a36Sopenharmony_ci <barrera general> <barrera general> 231562306a36Sopenharmony_ci LOAD evento_indicado if ((LOAD task->state) & TASK_NORMAL) 231662306a36Sopenharmony_ci STORE task->state 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_cidonde "task" es el subproceso que se está despertando y es igual al 231962306a36Sopenharmony_ci"current" (hilo actual) de la CPU 1. 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ciPara reiterar, se garantiza la ejecución de una barrera de memoria general 232262306a36Sopenharmony_cimediante wake_up() si algo está realmente despierto, pero de lo contrario 232362306a36Sopenharmony_cino existe tal garantía. Para entender esto, considere la siguiente 232462306a36Sopenharmony_cisecuencia de eventos, donde X e Y son ambos cero inicialmente: 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci CPU 1 CPU 2 232762306a36Sopenharmony_ci =============================== =============================== 232862306a36Sopenharmony_ci X = 1; Y = 1; 232962306a36Sopenharmony_ci smp_mb(); wake_up(); 233062306a36Sopenharmony_ci LOAD Y LOAD X 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ciSi ocurre una reactivación ("wakeup"), una (al menos) de las dos cargas 233362306a36Sopenharmony_cidebe ver 1. Si, por otro lado, no ocurre una reactivación, ambas cargas 233462306a36Sopenharmony_cipueden ver 0. 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ciwake_up_process() siempre ejecuta una barrera de memoria general. La 233762306a36Sopenharmony_cibarrera, de nuevo, ocurre antes de que se acceda al estado del hilo. En 233862306a36Sopenharmony_ciparticular, si wake_up(), en el fragmento anterior, fuera reemplazado por 233962306a36Sopenharmony_ciuna llamada a wake_up_process(), las dos cargas verían 1, garantizado. 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ciLas funciones de activación disponibles incluyen: 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci complete(); 234462306a36Sopenharmony_ci wake_up(); 234562306a36Sopenharmony_ci wake_up_all(); 234662306a36Sopenharmony_ci wake_up_bit(); 234762306a36Sopenharmony_ci wake_up_interruptible(); 234862306a36Sopenharmony_ci wake_up_interruptible_all(); 234962306a36Sopenharmony_ci wake_up_interruptible_nr(); 235062306a36Sopenharmony_ci wake_up_interruptible_poll(); 235162306a36Sopenharmony_ci wake_up_interruptible_sync(); 235262306a36Sopenharmony_ci wake_up_interruptible_sync_poll(); 235362306a36Sopenharmony_ci wake_up_locked(); 235462306a36Sopenharmony_ci wake_up_locked_poll(); 235562306a36Sopenharmony_ci wake_up_nr(); 235662306a36Sopenharmony_ci wake_up_poll(); 235762306a36Sopenharmony_ci wake_up_process(); 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ciEn términos de orden de la memoria, todas estas funciones proporcionan las 236062306a36Sopenharmony_cimismas garantías que un wake_up() (o más fuertes). 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci[!] Tenga en cuenta que las barreras de la memoria implicadas por el 236362306a36Sopenharmony_cidurmiente y el despierto _no_ ordenan varios stores antes del despertar con 236462306a36Sopenharmony_cirespecto a cargas de los valores guardados después de que el durmiente haya 236562306a36Sopenharmony_cillamado a set_current_state(). Por ejemplo, si el durmiente hace: 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 236862306a36Sopenharmony_ci if (evento_indicado) 236962306a36Sopenharmony_ci break; 237062306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 237162306a36Sopenharmony_ci hacer_algo(my_data); 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ciy el que despierta hace: 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci my_data = valor; 237662306a36Sopenharmony_ci evento_indicado = 1; 237762306a36Sopenharmony_ci wake_up(&event_wait_queue); 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_cino existe garantía de que el cambio a event_indicated sea percibido por 238062306a36Sopenharmony_ciel durmiente de manera que venga después del cambio a my_data. En tal 238162306a36Sopenharmony_cicircunstancia, el código en ambos lados debe sacar sus propias barreras de 238262306a36Sopenharmony_cimemoria entre los separados accesos a datos. Por lo tanto, el durmiente 238362306a36Sopenharmony_cianterior debería hacer: 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 238662306a36Sopenharmony_ci if (evento_indicado) { 238762306a36Sopenharmony_ci smp_rmb(); 238862306a36Sopenharmony_ci hacer_algo(my_data); 238962306a36Sopenharmony_ci } 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ciy el que despierta debería hacer: 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci my_data = value; 239462306a36Sopenharmony_ci smp_wmb(); 239562306a36Sopenharmony_ci evento_indicado = 1; 239662306a36Sopenharmony_ci wake_up(&event_wait_queue); 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ciFUNCIONES VARIAS 239962306a36Sopenharmony_ci---------------- 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ciOtras funciones que implican barreras: 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci (*) schedule() y similares implican barreras completas de memoria. 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci======================================== 240762306a36Sopenharmony_ciEFECTOS DE BARRERA ADQUIRIENDO INTRA-CPU 240862306a36Sopenharmony_ci======================================== 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ciEn los sistemas SMP, las primitivas de bloqueo proveen una forma más 241162306a36Sopenharmony_cisustancial de barrera: una que afecta el orden de acceso a la memoria en 241262306a36Sopenharmony_ciotras CPU, dentro del contexto de conflicto en cualquier bloqueo en 241362306a36Sopenharmony_ciparticular. 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ciADQUISICIÓN VS ACCESOS A MEMORIA 241762306a36Sopenharmony_ci-------------------------------- 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ciConsidere lo siguiente: el sistema tiene un par de spinlocks (M) y (Q), y 242062306a36Sopenharmony_citres CPU; entonces la siguiente secuencia de eventos debería ocurrir: 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci CPU 1 CPU 2 242362306a36Sopenharmony_ci =============================== =============================== 242462306a36Sopenharmony_ci WRITE_ONCE(*A, a); WRITE_ONCE(*E, e); 242562306a36Sopenharmony_ci ACQUIRE M ACQUIRE Q 242662306a36Sopenharmony_ci WRITE_ONCE(*B, b); WRITE_ONCE(*F, f); 242762306a36Sopenharmony_ci WRITE_ONCE(*C, c); WRITE_ONCE(*G, g); 242862306a36Sopenharmony_ci RELEASE M RELEASE Q 242962306a36Sopenharmony_ci WRITE_ONCE(*D, d); WRITE_ONCE(*H, h); 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ciEntonces no hay garantía sobre en qué orden verá la CPU 3 los accesos a *A 243262306a36Sopenharmony_cihasta que *H ocurra, además de las restricciones impuestas por los bloqueos 243362306a36Sopenharmony_ciseparados en las distintas CPUs. Podría, por ejemplo, ver: 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci *E, ACQUIRE M, ACQUIRE Q, *G, *C, *F, *A, *B, RELEASE Q, *D, *H, RELEASE M 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ciPero no verá ninguno de: 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci *B, *C or *D preceding ACQUIRE M 244062306a36Sopenharmony_ci *A, *B or *C following RELEASE M 244162306a36Sopenharmony_ci *F, *G or *H preceding ACQUIRE Q 244262306a36Sopenharmony_ci *E, *F or *G following RELEASE Q 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci======================================== 244562306a36Sopenharmony_ci¿DÓNDE SE NECESITAN BARRERAS DE MEMORIA? 244662306a36Sopenharmony_ci======================================== 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ciBajo operación normal, el re-ordenamiento de una operación de memoria 244962306a36Sopenharmony_cigeneralmente no va a suponer un problema, ya que para una pieza de código 245062306a36Sopenharmony_cilineal de un solo subproceso seguirá pareciendo que funciona correctamente, 245162306a36Sopenharmony_ciincluso si está en un kernel SMP. Existen, sin embargo, cuatro 245262306a36Sopenharmony_cicircunstancias en las que reordenar definitivamente _podría_ ser un 245362306a36Sopenharmony_ciproblema: 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci (*) Interacción entre procesadores. 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci (*) Operaciones atómicas. 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci (*) Acceso a dispositivos. 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci (*) Interrupciones. 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ciINTERACCIÓN ENTRE PROCESADORES 246562306a36Sopenharmony_ci------------------------------ 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ciCuando se da un sistema con más de un procesador, más de una CPU en el 246862306a36Sopenharmony_cisistema puede estar trabajando en el mismo conjunto de datos al mismo 246962306a36Sopenharmony_citiempo. Esto puede causar problemas de sincronización, y la forma habitual 247062306a36Sopenharmony_cide tratar con estos es utilizar cerrojos. Sin embargo, los cerrojos son 247162306a36Sopenharmony_cibastante caros, por lo que puede ser preferible operar sin el uso de un 247262306a36Sopenharmony_cicerrojo a ser posible. En cuyo caso, es posible que las operaciones que 247362306a36Sopenharmony_ciafectan a ambas CPU deban ordenarse cuidadosamente para evitar un 247462306a36Sopenharmony_cifuncionamiento incorrecto. 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ciConsidere, por ejemplo, la ruta lenta del semáforo R/W. Aquí hay un proceso 247762306a36Sopenharmony_cide espera en cola del semáforo, en virtud de que tiene una parte de su pila 247862306a36Sopenharmony_civinculada a la lista de procesos en espera del semáforo: 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci struct rw_semaphore { 248162306a36Sopenharmony_ci ... 248262306a36Sopenharmony_ci spinlock_t lock; 248362306a36Sopenharmony_ci struct list_head waiters; 248462306a36Sopenharmony_ci }; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci struct rwsem_waiter { 248762306a36Sopenharmony_ci struct list_head list; 248862306a36Sopenharmony_ci struct task_struct *task; 248962306a36Sopenharmony_ci }; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ciPara despertar a un proceso que espera ("waiter") en particular, las 249262306a36Sopenharmony_cifunciones up_read() o up_write() tienen que: 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci (1) leer el siguiente puntero del registro de este proceso que espera, 249562306a36Sopenharmony_ci para saber dónde está el registro del siguiente waiter; 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci (2) leer el puntero a la estructura de tareas del waiter; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci (3) borrar el puntero de la tarea para decirle al waiter que se le ha dado 250062306a36Sopenharmony_ci el semáforo; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci (4) llamar a wake_up_process() en la tarea; y 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci (5) liberar la referencia retenida en la estructura de tareas del waiter. 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ciEn otras palabras, tiene que realizar esta secuencia de eventos: 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci LOAD waiter->list.next; 250962306a36Sopenharmony_ci LOAD waiter->task; 251062306a36Sopenharmony_ci STORE waiter->task; 251162306a36Sopenharmony_ci CALL wakeup 251262306a36Sopenharmony_ci RELEASE task 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ciy si alguno de estos pasos ocurre fuera de orden, entonces todo puede que 251562306a36Sopenharmony_cifuncione defectuosamente. 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ciUna vez que se ha puesto en cola y soltado el bloqueo de semáforo, el 251862306a36Sopenharmony_ciproceso que espera no consigue el candado de nuevo; en cambio, solo espera 251962306a36Sopenharmony_cia que se borre su puntero de tarea antes de continuar. Dado que el registro 252062306a36Sopenharmony_ciestá en la pila del proceso que espera, esto significa que si el puntero de 252162306a36Sopenharmony_cila tarea se borra _antes_ de que se lea el siguiente puntero de la lista, 252262306a36Sopenharmony_ciotra CPU podría comenzar a procesar el proceso que espera y podría romper 252362306a36Sopenharmony_ciel stack del proceso que espera antes de que la función up*() tenga la 252462306a36Sopenharmony_cioportunidad de leer el puntero que sigue. 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ciConsidere entonces lo que podría suceder con la secuencia de eventos 252762306a36Sopenharmony_cianterior: 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci CPU 1 CPU 2 253062306a36Sopenharmony_ci =============================== =============================== 253162306a36Sopenharmony_ci down_xxx() 253262306a36Sopenharmony_ci Poner waiter en la "queue" (cola) 253362306a36Sopenharmony_ci Dormir 253462306a36Sopenharmony_ci up_yyy() 253562306a36Sopenharmony_ci LOAD waiter->task; 253662306a36Sopenharmony_ci STORE waiter->task; 253762306a36Sopenharmony_ci Despertado por otro evento 253862306a36Sopenharmony_ci <preempt> 253962306a36Sopenharmony_ci Reanudar el procesamiento 254062306a36Sopenharmony_ci down_xxx() regresa 254162306a36Sopenharmony_ci llamada a foo() 254262306a36Sopenharmony_ci foo() estropea *waiter 254362306a36Sopenharmony_ci </preempt> 254462306a36Sopenharmony_ci LOAD waiter->list.next; 254562306a36Sopenharmony_ci --- OOPS --- 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ciEsto podría solucionarse usando el bloqueo de semáforo, pero luego la 254862306a36Sopenharmony_cifunción down_xxx() tiene que obtener innecesariamente el spinlock 254962306a36Sopenharmony_cinuevamente, después de ser despertado el hilo. 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ciLa forma de lidiar con esto es insertar una barrera de memoria SMP general: 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci LOAD waiter->list.next; 255462306a36Sopenharmony_ci LOAD waiter->task; 255562306a36Sopenharmony_ci smp_mb(); 255662306a36Sopenharmony_ci STORE waiter->task; 255762306a36Sopenharmony_ci CALL wakeup 255862306a36Sopenharmony_ci RELEASE task 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ciEn este caso, la barrera garantiza que todos los accesos a memoria antes de 256162306a36Sopenharmony_cila barrera parecerán suceder antes de todos los accesos a memoria después 256262306a36Sopenharmony_cide dicha barrera con respecto a las demás CPU del sistema. _No_ garantiza 256362306a36Sopenharmony_cique todos los accesos a memoria antes de la barrera se completarán en el 256462306a36Sopenharmony_cimomento en que la instrucción de la barrera en sí se complete. 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ciEn un sistema UP, donde esto no sería un problema, la función smp_mb() es 256762306a36Sopenharmony_cisolo una barrera del compilador, asegurándose así de que el compilador 256862306a36Sopenharmony_ciemita las instrucciones en el orden correcto sin realmente intervenir en la 256962306a36Sopenharmony_ciCPU. Como solo hay un CPU, la lógica de orden de dependencias de esa CPU se 257062306a36Sopenharmony_ciencargará de todo lo demás. 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ciOPERACIONES ATÓMICAS 257462306a36Sopenharmony_ci-------------------- 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ciSi bien son, técnicamente, consideraciones de interacción entre 257762306a36Sopenharmony_ciprocesadores, las operaciones atómicas se destacan especialmente porque 257862306a36Sopenharmony_cialgunas de ellas implican barreras de memoria completa y algunas otras no, 257962306a36Sopenharmony_cipero se confía mucho en ellos en su conjunto a lo largo del kernel. 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ciConsulte Documentation/atomic_t.txt para obtener más información. 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ciACCESO A DISPOSITIVOS 258562306a36Sopenharmony_ci--------------------- 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ciUn driver puede ser interrumpido por su propia rutina de servicio de 258862306a36Sopenharmony_ciinterrupción y, por lo tanto, las dos partes del driver pueden interferir 258962306a36Sopenharmony_cicon los intentos de controlar o acceder al dispositivo. 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ciEsto puede aliviarse, al menos en parte, desactivando las interrupciones 259262306a36Sopenharmony_cilocales (una forma de bloqueo), de modo que las operaciones críticas sean 259362306a36Sopenharmony_citodas contenidas dentro la sección de interrupción desactivada en el 259462306a36Sopenharmony_cicontrolador. Mientras la interrupción del driver está ejecutando la rutina, 259562306a36Sopenharmony_cies posible que el "core" del controlador no se ejecute en la misma CPU y no 259662306a36Sopenharmony_cise permita que su interrupción vuelva a ocurrir hasta que la interrupción 259762306a36Sopenharmony_ciactual haya sido resuelta, por lo tanto, el controlador de interrupción no 259862306a36Sopenharmony_cinecesita bloquearse contra esto. 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ciSin embargo, considere un driver que estaba hablando con una tarjeta 260162306a36Sopenharmony_ciethernet que tiene un registro de direcciones y un registro de datos. Si 260262306a36Sopenharmony_ciel core de ese controlador habla con la tarjeta estando en desactivación de 260362306a36Sopenharmony_ciinterrupción y luego se invoca el controlador de interrupción del 260462306a36Sopenharmony_cicontrolador: 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci IRQ LOCALES DESACTIVADAS 260762306a36Sopenharmony_ci writew(ADDR, 3); 260862306a36Sopenharmony_ci writew(DATA, y); 260962306a36Sopenharmony_ci IRQ LOCALES ACTIVADAS 261062306a36Sopenharmony_ci <interrupción> 261162306a36Sopenharmony_ci writew(ADDR, 4); 261262306a36Sopenharmony_ci q = readw(DATA); 261362306a36Sopenharmony_ci </interrupción> 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ciEl almacenamiento en el registro de datos puede ocurrir después del segundo 261662306a36Sopenharmony_cialmacenamiento en el registro de direcciones si las reglas de orden son lo 261762306a36Sopenharmony_cisuficientemente relajadas: 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci STORE *ADDR = 3, STORE *ADDR = 4, STORE *DATA = y, q = LOAD *DATA 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ciSi se relajan las reglas de orden, se debe asumir que los accesos 262262306a36Sopenharmony_cirealizados dentro de la sección con interrupción deshabilitada pueden 262362306a36Sopenharmony_cifiltrarse fuera de esta y pueden intercalarse con accesos realizados en una 262462306a36Sopenharmony_ciinterrupción - y viceversa - a menos que se utilicenn barreras implícita o 262562306a36Sopenharmony_ciexplícitas. 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ciNormalmente, esto no será un problema porque los accesos de E/S realizados 262862306a36Sopenharmony_cidentro de tales secciones incluirán operaciones de carga síncronas en 262962306a36Sopenharmony_ciregistros E/S estrictamente ordenados, que forman barreras de E/S 263062306a36Sopenharmony_ciimplícitas. 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ciUna situación similar puede ocurrir entre una rutina de interrupción y dos 263462306a36Sopenharmony_cirutinas ejecutándose en separadas CPU que se comunican entre sí. Si tal 263562306a36Sopenharmony_cicaso es probable, entonces se deben usar bloqueos de desactivación de 263662306a36Sopenharmony_ciinterrupciones para garantizar el orden. 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci===================================== 264062306a36Sopenharmony_ci Efectos de barrera de E/S del kernel 264162306a36Sopenharmony_ci===================================== 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ciLa interfaz con periféricos a través de accesos de E/S es profundamente 264462306a36Sopenharmony_ciespecífica para cada arquitectura y dispositivo. Por lo tanto, los drivers 264562306a36Sopenharmony_cique son inherentemente no portátiles pueden depender de comportamientos 264662306a36Sopenharmony_ciespecíficos de sus sistemas de destino, con el fin de lograr la 264762306a36Sopenharmony_cisincronización de la manera más ligera posible. Para drivers que deseen ser 264862306a36Sopenharmony_ciportátiles entre múltiples arquitecturas e implementaciones de bus, el 264962306a36Sopenharmony_cikernel ofrece una serie de funciones de acceso que proporcionan varios 265062306a36Sopenharmony_cigrados de garantías de orden: 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci (*) readX(), writeX(): 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci Las funciones de acceso MMIO readX() y writeX() usan un puntero al 265562306a36Sopenharmony_ci periférico al que se accede como un parámetro __iomem *. para punteros 265662306a36Sopenharmony_ci asignados los atributos de E/S predeterminados (por ejemplo, los 265762306a36Sopenharmony_ci devueltos por ioremap()), las garantías de orden son las siguientes: 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci 1. Se ordenan todos los accesos readX() y writeX() a un mismo periférico 266062306a36Sopenharmony_ci entre estos. Esto asegura que los registros de acceso MMIO por el 266162306a36Sopenharmony_ci mismo subproceso de la CPU a un dispositivo en particular llegarán en 266262306a36Sopenharmony_ci el orden del programa. 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci 2. Se ordena un writeX() emitido por un subproceso de CPU que contiene un 266562306a36Sopenharmony_ci spinlock antes de un writeX() al mismo periférico desde otro 266662306a36Sopenharmony_ci subproceso de CPU, si emitido después de una adquisición posterior del 266762306a36Sopenharmony_ci mismo spinlock. Esto garantiza que ese registro MMIO escribe en un 266862306a36Sopenharmony_ci dispositivo en particular, mientras que se obtiene un spinlock en un 266962306a36Sopenharmony_ci orden consistente con las adquisiciones del cerrojo. 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci 3. Un writeX() por un subproceso de la CPU al periférico primero esperará 267262306a36Sopenharmony_ci a la finalización de todas las escrituras anteriores en la memoria 267362306a36Sopenharmony_ci emitidas por, o bien propagadas por, el mismo subproceso. Esto asegura 267462306a36Sopenharmony_ci que las escrituras de la CPU a un búfer DMA de salida asignadas por 267562306a36Sopenharmony_ci dma_alloc_coherent() serán visibles para un motor ("engine") DMA 267662306a36Sopenharmony_ci cuando la CPU escriba en sus registros de control MMIO, para activar 267762306a36Sopenharmony_ci la transferencia. 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci 4. Un readX() de un subproceso del CPU, desde el periférico, se 268062306a36Sopenharmony_ci completará antes de que cualquier lectura subsiguiente de memoria por 268162306a36Sopenharmony_ci el mismo subproceso pueda comenzar. Esto asegura que las lecturas de 268262306a36Sopenharmony_ci la CPU desde un búfer DMA entrantes asignadas por 268362306a36Sopenharmony_ci dma_alloc_coherent(), no verán datos obsoletos después de leer el 268462306a36Sopenharmony_ci registro de estado MMIO del motor DMA, para establecer que la 268562306a36Sopenharmony_ci transferencia DMA se haya completado. 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci 5. Un readX() por un subproceso del CPU, desde el periférico, se 268862306a36Sopenharmony_ci completará antes de que cualquier bucle delay() subsiguiente pueda 268962306a36Sopenharmony_ci comenzar a ejecutarse en el mismo subproceso. Esto asegura que dos 269062306a36Sopenharmony_ci escrituras del CPU a registros MMIO en un periférico llegarán al menos 269162306a36Sopenharmony_ci con 1us de diferencia, si la primera escritura se lee inmediatamente 269262306a36Sopenharmony_ci de vuelta con readX() y se llama a udelay(1) antes del segundo 269362306a36Sopenharmony_ci writeX(): 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci writel(42, DEVICE_REGISTER_0); // Llega al dispositivo ... 269662306a36Sopenharmony_ci readl(DEVICE_REGISTER_0); 269762306a36Sopenharmony_ci udelay(1); 269862306a36Sopenharmony_ci writel(42, DEVICE_REGISTER_1); // al menos 1us antes de esto.... 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ciLas propiedades de orden de los punteros __iomem obtenidos con valores de 270162306a36Sopenharmony_ciatributos que no sean los valores por defecto (por ejemplo, los devueltos 270262306a36Sopenharmony_cipor ioremap_wc()) son específicos de la arquitectura subyacente y, por lo 270362306a36Sopenharmony_citanto, las garantías enumeradas anteriormente no pueden por lo general ser 270462306a36Sopenharmony_ciaseguradas para accesos a este tipo de "mappings" (asignaciones). 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci (*) readX_relaxed(), writeX_relaxed(): 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci Son similares a readX() y writeX(), pero proporcionan una garantía de 270962306a36Sopenharmony_ci orden de memoria más débil. Específicamente, no garantizan orden con 271062306a36Sopenharmony_ci respecto al bloqueo, los accesos normales a la memoria o los bucles 271162306a36Sopenharmony_ci delay() (es decir, los puntos 2-5 arriba) pero todavía se garantiza que 271262306a36Sopenharmony_ci se ordenarán con respecto a otros accesos desde el mismo hilo de la CPU, 271362306a36Sopenharmony_ci al mismo periférico, cuando se opera en punteros __iomem asignados con el 271462306a36Sopenharmony_ci valor predeterminado para los atributos de E/S. 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci (*) readsX(), writesX(): 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci Los puntos de entrada readsX() y writesX() MMIO están diseñados para 271962306a36Sopenharmony_ci acceder FIFOs mapeados en memoria y basados en registros que residen en 272062306a36Sopenharmony_ci periféricos, que no son capaces de realizar DMA. Por tanto, sólo 272162306a36Sopenharmony_ci proporcionan garantías de orden readX_relaxed() y writeX_relaxed(), como 272262306a36Sopenharmony_ci se documentó anteriormente. 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci (*) inX(), outX(): 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci Los puntos de entrada inX() y outX() están destinados a acceder a mapas 272762306a36Sopenharmony_ci de puertos "legacy" (antiguos) de periféricos de E/S, que pueden requerir 272862306a36Sopenharmony_ci instrucciones especiales en algunas arquitecturas (especialmente, en 272962306a36Sopenharmony_ci x86). El número de puerto del periférico que se está accedido se pasa 273062306a36Sopenharmony_ci como un argumento. 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci Dado que muchas arquitecturas de CPU acceden finalmente a estos 273362306a36Sopenharmony_ci periféricos a través de un mapeo interno de memoria virtual, las 273462306a36Sopenharmony_ci garantías de orden portátiles proporcionadas por inX() y outX() son las 273562306a36Sopenharmony_ci mismas que las proporcionadas por readX() y writeX(), respectivamente, al 273662306a36Sopenharmony_ci acceder a una asignación con los valores de atributos de E/S 273762306a36Sopenharmony_ci predeterminados (los que haya por defecto). 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci Los drivers de dispositivos pueden esperar que outX() emita una 274062306a36Sopenharmony_ci transacción de escritura no publicada, que espera una respuesta de 274162306a36Sopenharmony_ci finalización del periférico de E/S antes de regresar. Esto no está 274262306a36Sopenharmony_ci garantizado por todas las arquitecturas y por lo tanto no forma parte de 274362306a36Sopenharmony_ci la semántica de orden portátil. 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci (*) insX(), outsX(): 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci Como arriba, los puntos de entrada insX() y outsX() proporcionan el mismo 274862306a36Sopenharmony_ci orden garantizado por readsX() y writesX() respectivamente, al acceder a 274962306a36Sopenharmony_ci un mapping con los atributos de E/S predeterminados. 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci (*) ioreadX(), iowriteX(): 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci Estos funcionarán adecuadamente para el tipo de acceso que realmente están 275462306a36Sopenharmony_ci haciendo, ya sea inX()/outX() o readX()/writeX(). 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ciCon la excepción de los puntos de entrada (insX(), outsX(), readsX() y 275762306a36Sopenharmony_ciwritesX()), todo lo anterior supone que el periférico subyacente es 275862306a36Sopenharmony_cilittle-endian y, por lo tanto, realizará operaciones de intercambio de 275962306a36Sopenharmony_cibytes en arquitecturas big-endian. 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci=========================================== 276362306a36Sopenharmony_ciMODELO DE ORDEN MÍNIMO DE EJECUCIÓN ASUMIDO 276462306a36Sopenharmony_ci=========================================== 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ciDebe suponerse que la CPU conceptual está débilmente ordenada, pero que 276762306a36Sopenharmony_cimantiene la apariencia de causalidad del programa con respecto a sí misma. 276862306a36Sopenharmony_ciAlgunas CPU (como i386 o x86_64) están más limitadas que otras (como 276962306a36Sopenharmony_cipowerpc o frv), por lo que el caso más relajado (es decir, DEC Alpha) se 277062306a36Sopenharmony_cidebe asumir fuera de código específico de arquitectura. 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ciEsto significa que se debe considerar que la CPU ejecutará su flujo de 277362306a36Sopenharmony_ciinstrucciones en el orden que se quiera - o incluso en paralelo - siempre 277462306a36Sopenharmony_cique si una instrucción en el flujo depende de una instrucción anterior, 277562306a36Sopenharmony_cientonces dicha instrucción anterior debe ser lo suficientemente completa[*] 277662306a36Sopenharmony_ciantes de que la posterior instrucción puede proceder; en otras palabras: 277762306a36Sopenharmony_cisiempre que la apariencia de causalidad se mantenga. 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci [*] Algunas instrucciones tienen más de un efecto, como cambiar el 278062306a36Sopenharmony_ci código de condición, cambio de registros o cambio de memoria - y 278162306a36Sopenharmony_ci distintas instrucciones pueden depender de diferentes efectos. 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ciUna CPU puede también descartar cualquier secuencia de instrucciones que 278462306a36Sopenharmony_citermine sin tener efecto final. Por ejemplo, si dos instrucciones 278562306a36Sopenharmony_ciadyacentes cargan un valor inmediato en el mismo registro, la primera puede 278662306a36Sopenharmony_cidescartarse. 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ciDe manera similar, se debe suponer que el compilador podría reordenar la 279062306a36Sopenharmony_cicorriente de instrucciones de la manera que crea conveniente, nuevamente 279162306a36Sopenharmony_cisiempre que la apariencia de causalidad se mantenga. 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci===================================== 279562306a36Sopenharmony_ciEFECTOS DE LA MEMORIA CACHÉ DE LA CPU 279662306a36Sopenharmony_ci===================================== 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ciLa forma en que se perciben las operaciones de memoria caché en todo el 279962306a36Sopenharmony_cisistema se ve afectada, hasta cierto punto, por los cachés que se 280062306a36Sopenharmony_ciencuentran entre las CPU y la memoria, y por el sistema de coherencia en 280162306a36Sopenharmony_cimemoria que mantiene la consistencia de estado en el sistema. 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ciEn cuanto a la forma en que una CPU interactúa con otra parte del sistema a 280462306a36Sopenharmony_citravés del caché, el sistema de memoria tiene que incluir los cachés de la 280562306a36Sopenharmony_ciCPU y barreras de memoria, que en su mayor parte actúan en la interfaz 280662306a36Sopenharmony_cientre la CPU y su caché (las barreras de memoria lógicamente actúan sobre 280762306a36Sopenharmony_cila línea de puntos en el siguiente diagrama): 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci <--- CPU ---> : <----------- Memoria -----------> 281062306a36Sopenharmony_ci : 281162306a36Sopenharmony_ci +--------+ +--------+ : +--------+ +-----------+ 281262306a36Sopenharmony_ci | Core | | Cola | : | Cache | | | +---------+ 281362306a36Sopenharmony_ci | CPU | | de | : | CPU | | | | | 281462306a36Sopenharmony_ci | |--->| acceso |----->| |<-->| | | | 281562306a36Sopenharmony_ci | | | a | : | | | |--->| Memoria | 281662306a36Sopenharmony_ci | | | memoria| : | | | | | | 281762306a36Sopenharmony_ci +--------+ +--------+ : +--------+ | Mecanismo | | | 281862306a36Sopenharmony_ci : | de | +---------+ 281962306a36Sopenharmony_ci : | Coherencia| 282062306a36Sopenharmony_ci : | de la | +--------+ 282162306a36Sopenharmony_ci +--------+ +--------+ : +--------+ | cache | | | 282262306a36Sopenharmony_ci | Core | | Cola | : | Cache | | | | | 282362306a36Sopenharmony_ci | CPU | | de | : | CPU | | |--->| Dispos | 282462306a36Sopenharmony_ci | |--->| acceso |----->| |<-->| | | itivo | 282562306a36Sopenharmony_ci | | | a | : | | | | | | 282662306a36Sopenharmony_ci | | | memoria| : | | | | +--------+ 282762306a36Sopenharmony_ci +--------+ +--------+ : +--------+ +-----------+ 282862306a36Sopenharmony_ci : 282962306a36Sopenharmony_ci : 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ciAunque es posible que una carga o store en particular no aparezca fuera de 283262306a36Sopenharmony_cila CPU que lo emitió, ya que puede haber sido satisfecha dentro del propio 283362306a36Sopenharmony_cicaché de la CPU, seguirá pareciendo como si el acceso total a la memoria 283462306a36Sopenharmony_cihubiera tenido lugar para las otras CPUs, ya que los mecanismos de 283562306a36Sopenharmony_cicoherencia de caché migrarán la cacheline sobre la CPU que accede y se 283662306a36Sopenharmony_cipropagarán los efectos en caso de conflicto. 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ciEl núcleo de la CPU puede ejecutar instrucciones en el orden que considere 283962306a36Sopenharmony_ciadecuado, siempre que parezca mantenerse la causalidad esperada del 284062306a36Sopenharmony_ciprograma. Algunas de las instrucciones generan operaciones de carga y 284162306a36Sopenharmony_cialmacenamiento que luego van a la cola de accesos a memoria a realizar. El 284262306a36Sopenharmony_cinúcleo puede colocarlos en la cola en cualquier orden que desee, y 284362306a36Sopenharmony_cicontinuar su ejecución hasta que se vea obligado a esperar que una 284462306a36Sopenharmony_ciinstrucción sea completada. 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ciDe lo que se ocupan las barreras de la memoria es de controlar el orden en 284762306a36Sopenharmony_cique los accesos cruzan, desde el lado de la CPU, hasta el lado de memoria, 284862306a36Sopenharmony_ciy el orden en que los otros observadores perciben los efectos en el sistema 284962306a36Sopenharmony_cique sucedan por esto. 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci[!] Las barreras de memoria _no_ son necesarias dentro de una CPU 285262306a36Sopenharmony_cideterminada, ya que las CPU siempre ven sus propias cargas y stores como si 285362306a36Sopenharmony_cihubieran sucedido en el orden del programa. 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci[!] Los accesos a MMIO u otros dispositivos pueden pasar por alto el 285662306a36Sopenharmony_cisistema de caché. Esto depende de las propiedades de la ventana de memoria 285762306a36Sopenharmony_cia través de la cual se accede a los dispositivos y/o el uso de 285862306a36Sopenharmony_ciinstrucciones especiales de comunicación con dispositivo que pueda tener la 285962306a36Sopenharmony_ciCPU. 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ciCOHERENCIA DE CACHÉ FRENTE A DMA 286362306a36Sopenharmony_ci--------------------------------- 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ciNo todos los sistemas mantienen coherencia de caché con respecto a los 286662306a36Sopenharmony_cidispositivos que realizan DMA. En tales casos, un dispositivo que intente 286762306a36Sopenharmony_ciDMA puede obtener datos obsoletos de la RAM, porque las líneas de caché 286862306a36Sopenharmony_ci"sucias" pueden residir en los cachés de varias CPU, y es posible que no 286962306a36Sopenharmony_cise hayan vuelto a escribir en la RAM todavía. Para hacer frente a esto, la 287062306a36Sopenharmony_ciparte apropiada del kernel debe vaciar los bits superpuestos de caché en 287162306a36Sopenharmony_cicada CPU (y tal vez también invalidarlos). 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ciAdemás, los datos enviados por DMA a RAM, por un dispositivo, pueden ser 287462306a36Sopenharmony_cisobrescritos por líneas de caché sucias que se escriben de nuevo en la RAM 287562306a36Sopenharmony_cidesde el caché de una CPU, después de que el dispositivo haya puesto sus 287662306a36Sopenharmony_cipropios datos, o las líneas de caché presentes en el caché de la CPU pueden 287762306a36Sopenharmony_cisimplemente ocultar el hecho de que la memoria RAM se haya actualizado, 287862306a36Sopenharmony_cihasta el momento en que la caché se descarta de la memoria caché de la CPU 287962306a36Sopenharmony_ciy se vuelve a cargar. Para hacer frente a esto, la parte apropiada del 288062306a36Sopenharmony_cikernel debe invalidar los bits superpuestos del caché en cada CPU. 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ciConsulte Documentation/core-api/cachetlb.rst para obtener más información 288362306a36Sopenharmony_cisobre administración de la memoria caché. 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ciCOHERENCIA DE CACHÉ FRENTE A MMIO 288762306a36Sopenharmony_ci--------------------------------- 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ciLa E/S mapeada en memoria generalmente se lleva a cabo a través de 289062306a36Sopenharmony_ciubicaciones de memoria que forman parte de una ventana del espacio de 289162306a36Sopenharmony_cimemoria de la CPU, que tiene diferentes propiedades asignadas que la 289262306a36Sopenharmony_civentana habitual dirigida a RAM. 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ciEntre dichas propiedades, suele existir el hecho de que tales accesos 289562306a36Sopenharmony_cieluden el almacenamiento en caché por completo e ir directamente a los 289662306a36Sopenharmony_cibuses del dispositivo. Esto significa que los accesos MMIO pueden, en 289762306a36Sopenharmony_ciefecto, superar los accesos a la memoria caché que se emitieron 289862306a36Sopenharmony_cianteriormente. Una barrera de memoria no es suficiente en tal caso, sino 289962306a36Sopenharmony_cique el caché debe ser vaciado entre la escritura de la memoria caché, y el 290062306a36Sopenharmony_ciacceso MMIO, si los dos son de cualquier manera dependiente. 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci======================= 290462306a36Sopenharmony_ciCOSAS QUE HACEN LAS CPU 290562306a36Sopenharmony_ci======================= 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ciUn programador podría dar por sentado que la CPU realizará las operaciones 290862306a36Sopenharmony_cide memoria exactamente en el orden especificado, de modo que si a la CPU se 290962306a36Sopenharmony_cientrega, por ejemplo, el siguiente fragmento de código a ejecutar: 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_ci a = READ_ONCE(*A); 291262306a36Sopenharmony_ci WRITE_ONCE(*B, b); 291362306a36Sopenharmony_ci c = READ_ONCE(*C); 291462306a36Sopenharmony_ci d = READ_ONCE(*D); 291562306a36Sopenharmony_ci WRITE_ONCE(*E, e); 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ciesperarían entonces que la CPU complete la operación de memoria para cada 291862306a36Sopenharmony_ciinstrucción antes de pasar a la siguiente, lo que lleva a una definida 291962306a36Sopenharmony_cisecuencia de operaciones vistas por observadores externos en el sistema: 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci LOAD *A, STORE *B, LOAD *C, LOAD *D, STORE *E. 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ciLa realidad es, por supuesto, mucho más intrincada. Para muchas CPU y 292462306a36Sopenharmony_cicompiladores, la anterior suposición no se sostiene porque: 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci (*) es más probable que las cargas deban completarse de inmediato para 292762306a36Sopenharmony_ci permitir progreso en la ejecución, mientras que los stores a menudo se 292862306a36Sopenharmony_ci pueden aplazar sin problema; 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci (*) las cargas se pueden hacer especulativamente, y el resultado es 293162306a36Sopenharmony_ci descartado si resulta innecesario; 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci (*) las cargas se pueden hacer de forma especulativa, lo que lleva a que 293462306a36Sopenharmony_ci se haya obtenido el resultado en el momento equivocado de la secuencia 293562306a36Sopenharmony_ci de eventos esperada; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci (*) el orden de los accesos a memoria se puede reorganizar para promover 293862306a36Sopenharmony_ci un mejor uso de los buses y cachés de la CPU; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci (*) las cargas y los stores se pueden combinar para mejorar el rendimiento 294162306a36Sopenharmony_ci cuando se habla con memoria o hardware de E/S, que puede realizar 294262306a36Sopenharmony_ci accesos por lotes a ubicaciones adyacentes, reduciendo así los costes 294362306a36Sopenharmony_ci de configuración de transacciones (la memoria y los dispositivos PCI 294462306a36Sopenharmony_ci pueden ambos pueden hacer esto); y 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci (*) la caché de datos de la CPU puede afectar al orden, y mientras sus 294762306a36Sopenharmony_ci mecanismos de coherencia pueden aliviar esto, una vez que el store 294862306a36Sopenharmony_ci haya accedido al caché- no hay garantía de que la gestión de la 294962306a36Sopenharmony_ci coherencia se propague en orden a otras CPU. 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ciEntonces, digamos que lo que otra CPU podría observar en el fragmento de 295262306a36Sopenharmony_cicódigo anterior es: 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci LOAD *A, ..., LOAD {*C,*D}, STORE *E, STORE *B 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci (Donde "LOAD {*C,*D}" es una carga combinada) 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ciSin embargo, se garantiza que una CPU es autoconsistente: verá que sus 296062306a36Sopenharmony_ci _propios_ accesos parecen estar correctamente ordenados, sin necesidad de 296162306a36Sopenharmony_cibarrera de memoria. Por ejemplo con el siguiente código: 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci U = READ_ONCE(*A); 296462306a36Sopenharmony_ci WRITE_ONCE(*A, V); 296562306a36Sopenharmony_ci WRITE_ONCE(*A, W); 296662306a36Sopenharmony_ci X = READ_ONCE(*A); 296762306a36Sopenharmony_ci WRITE_ONCE(*A, Y); 296862306a36Sopenharmony_ci Z = READ_ONCE(*A); 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ciy asumiendo que no hay intervención de una influencia externa, se puede 297162306a36Sopenharmony_cisuponer que el resultado final se parecerá a: 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci U == el valor original de *A 297462306a36Sopenharmony_ci X == W 297562306a36Sopenharmony_ci Z == Y 297662306a36Sopenharmony_ci *A == Y 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ciEl código anterior puede hacer que la CPU genere la secuencia completa de 297962306a36Sopenharmony_ciaccesos de memoria: 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci U=LOAD *A, STORE *A=V, STORE *A=W, X=LOAD *A, STORE *A=Y, Z=LOAD *A 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_cien ese orden, pero, sin intervención, la secuencia puede contener casi 298462306a36Sopenharmony_cicualquier combinación de elementos combinados o descartados, siempre que la 298562306a36Sopenharmony_ciperspectiva del programa del mundo siga siendo consistente. Tenga en cuenta 298662306a36Sopenharmony_cique READ_ONCE() y WRITE_ONCE() -no- son opcionales en el ejemplo anterior, 298762306a36Sopenharmony_ciya que hay arquitecturas donde una CPU determinada podría reordenar cargas 298862306a36Sopenharmony_cisucesivas en la misma ubicación. En tales arquitecturas, READ_ONCE() y 298962306a36Sopenharmony_ciWRITE_ONCE() hacen lo que sea necesario para evitar esto, por ejemplo, en 299062306a36Sopenharmony_ciItanium los casts volátiles utilizados por READ_ONCE() y WRITE_ONCE() hacen 299162306a36Sopenharmony_cique GCC emita las instrucciones especiales ld.acq y st.rel 299262306a36Sopenharmony_ci(respectivamente) que impiden dicha reordenación. 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ciEl compilador también puede combinar, descartar o diferir elementos de la 299562306a36Sopenharmony_cisecuencia antes incluso de que la CPU los vea. 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ciPor ejemplo: 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci *A = V; 300062306a36Sopenharmony_ci *A = W; 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_cipuede reducirse a: 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci *A = W; 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ciya que, sin una barrera de escritura o WRITE_ONCE(), puede que se asuma 300762306a36Sopenharmony_cique el efecto del almacenamiento de V a *A se pierde. Similarmente: 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci *A = Y; 301062306a36Sopenharmony_ci Z = *A; 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_cipuede, sin una barrera de memoria o un READ_ONCE() y WRITE_ONCE(), esto 301362306a36Sopenharmony_cisea reducido a: 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci *A = Y; 301662306a36Sopenharmony_ci Z = Y; 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ciy la operación LOAD nunca aparezca fuera de la CPU. 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_ciY LUEGO ESTÁ EL ALFA 302262306a36Sopenharmony_ci-------------------- 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ciLa CPU DEC Alpha es una de las CPU más relajadas que existen. No solo eso, 302562306a36Sopenharmony_cialgunas versiones de la CPU Alpha tienen un caché de datos dividido, lo que 302662306a36Sopenharmony_ciles permite tener dos líneas de caché relacionadas semánticamente, 302762306a36Sopenharmony_ciactualizadas en momentos separados. Aquí es donde la barrera de dependencia 302862306a36Sopenharmony_cide dirección realmente se vuelve necesaria, ya que se sincronizan ambos 302962306a36Sopenharmony_cicachés con el sistema de coherencia de memoria, lo que hace que parezca un 303062306a36Sopenharmony_cicambio en el puntero, frente a que los nuevos datos se produzcan en el 303162306a36Sopenharmony_ciorden correcto. 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ciAlpha define el modelo de memoria del kernel Linux, aunque a partir de 303462306a36Sopenharmony_civ4.15, la adición al kernel de Linux de smp_mb() a READ_ONCE() en Alpha 303562306a36Sopenharmony_ciredujo en gran medida su impacto en el modelo de memoria. 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ciGUESTS DE MÁQUINAS VIRTUALES 303962306a36Sopenharmony_ci----------------------------- 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ciLos "guests" (invitados) que se ejecutan en máquinas virtuales pueden verse 304262306a36Sopenharmony_ciafectados por los efectos de SMP incluso si el "host" (huésped) en sí se 304362306a36Sopenharmony_cicompila sin compatibilidad con SMP. Este es un efecto de la interacción con 304462306a36Sopenharmony_ciun host SMP mientras ejecuta un kernel UP. El uso obligatorio de barreras 304562306a36Sopenharmony_cipara este caso de uso sería posible, pero a menudo no son óptimas. 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ciPara hacer frente a este caso de manera óptima, están disponibles macros de 304862306a36Sopenharmony_cibajo nivel virt_mb() etc. Estas tienen el mismo efecto que smp_mb(), etc. 304962306a36Sopenharmony_cicuando SMP está habilitado, pero generan código idéntico para sistemas SMP 305062306a36Sopenharmony_ciy no SMP. Por ejemplo, los invitados de máquinas virtuales debería usar 305162306a36Sopenharmony_civirt_mb() en lugar de smp_mb() al sincronizar contra un (posiblemente SMP) 305262306a36Sopenharmony_cianfitrión. 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ciEstos son equivalentes a sus contrapartes smp_mb() etc. en todos los demás 305562306a36Sopenharmony_ciaspectos, en particular, no controlan los efectos MMIO: para controlar los 305662306a36Sopenharmony_ciefectos MMIO, utilice barreras obligatorias. 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci================ 306062306a36Sopenharmony_ciEJEMPLOS DE USOS 306162306a36Sopenharmony_ci================ 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ciBUFFERS CIRCULARES 306462306a36Sopenharmony_ci------------------ 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ciLas barreras de memoria se pueden utilizar para implementar almacenamiento 306762306a36Sopenharmony_cien búfer circular, sin necesidad de un cerrojo para serializar al productor 306862306a36Sopenharmony_cicon el consumidor. Vea: 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci Documentation/core-api/circular-buffers.rst 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_cipara más detalles. 307362306a36Sopenharmony_ci 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci=========== 307662306a36Sopenharmony_ciREFERENCIAS 307762306a36Sopenharmony_ci=========== 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ciAlpha AXP Architecture Reference Manual, Segunda Edición (por Sites & Witek, 308062306a36Sopenharmony_ciDigital Press) 308162306a36Sopenharmony_ci Capítulo 5.2: Physical Address Space Characteristics 308262306a36Sopenharmony_ci Capítulo 5.4: Caches and Write Buffers 308362306a36Sopenharmony_ci Capítulo 5.5: Data Sharing 308462306a36Sopenharmony_ci Capítulo 5.6: Read/Write Ordering 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ciAMD64 Architecture Programmer's Manual Volumen 2: System Programming 308762306a36Sopenharmony_ci Capítulo 7.1: Memory-Access Ordering 308862306a36Sopenharmony_ci Capítulo 7.4: Buffering and Combining Memory Writes 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ciARM Architecture Reference Manual (ARMv8, for ARMv8-A architecture profile) 309162306a36Sopenharmony_ci Capítulo B2: The AArch64 Application Level Memory Model 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ciIA-32 Intel Architecture Software Developer's Manual, Volumen 3: 309462306a36Sopenharmony_ciSystem Programming Guide 309562306a36Sopenharmony_ci Capítulo 7.1: Locked Atomic Operations 309662306a36Sopenharmony_ci Capítulo 7.2: Memory Ordering 309762306a36Sopenharmony_ci Capítulo 7.4: Serializing Instructions 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ciThe SPARC Architecture Manual, Version 9 310062306a36Sopenharmony_ci Capítulo 8: Memory Models 310162306a36Sopenharmony_ci Appendix D: Formal Specification of the Memory Models 310262306a36Sopenharmony_ci Appendix J: Programming with the Memory Models 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ciStorage in the PowerPC (por Stone and Fitzgerald) 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ciUltraSPARC Programmer Reference Manual 310762306a36Sopenharmony_ci Capítulo 5: Memory Accesses and Cacheability 310862306a36Sopenharmony_ci Capítulo 15: Sparc-V9 Memory Models 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ciUltraSPARC III Cu User's Manual 311162306a36Sopenharmony_ci Capítulo 9: Memory Models 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ciUltraSPARC IIIi Processor User's Manual 311462306a36Sopenharmony_ci Capítulo 8: Memory Models 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ciUltraSPARC Architecture 2005 311762306a36Sopenharmony_ci Capítulo 9: Memory 311862306a36Sopenharmony_ci Appendix D: Formal Specifications of the Memory Models 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ciUltraSPARC T1 Supplement to the UltraSPARC Architecture 2005 312162306a36Sopenharmony_ci Capítulo 8: Memory Models 312262306a36Sopenharmony_ci Appendix F: Caches and Cache Coherency 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ciSolaris Internals, Core Kernel Architecture, p63-68: 312562306a36Sopenharmony_ci Capítulo 3.3: Hardware Considerations for Locks and 312662306a36Sopenharmony_ci Synchronization 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ciUnix Systems for Modern Architectures, Symmetric Multiprocessing and Caching 312962306a36Sopenharmony_cifor Kernel Programmers: 313062306a36Sopenharmony_ci Capítulo 13: Other Memory Models 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ciIntel Itanium Architecture Software Developer's Manual: Volumen 1: 313362306a36Sopenharmony_ci Sección 2.6: Speculation 313462306a36Sopenharmony_ci Sección 4.4: Memory Access 3135