From nobody Fri Sep 19 05:32:46 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 701FDC433FE for ; Mon, 28 Nov 2022 15:24:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231818AbiK1PYE (ORCPT ); Mon, 28 Nov 2022 10:24:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60166 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230376AbiK1PXd (ORCPT ); Mon, 28 Nov 2022 10:23:33 -0500 Received: from NAM11-CO1-obe.outbound.protection.outlook.com (mail-co1nam11on2057.outbound.protection.outlook.com [40.107.220.57]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A23D310C9; Mon, 28 Nov 2022 07:23:29 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Q4LCC/s0mOoS1D9oC4FhwQ1XNkBjQ8RZxcnq4XqHstXHJIQylmrV1sbv2KVOv1/P9DVL56fDu3P/RHNAEhVThZHC7gaI53LRFkpmCkU6ro/OgTrg1oDg5pUzhdIJQGg6MkzMK/t0tFj/edwQHNQIv6AuO0SBhCW10lfF+A5pfqxEoi7XCHWiFa6Pn8YvQqxcyKdPTiv8cEfq/vUr9w8zW253kmbWKL4H9UVFHc+3g4ZTnXc202PH461LcuFrOKIa4uhiK82XDVDUHSyu0bKGKk2+a+s+Dzto4lGDZgPPvE3vcC4sCCU//RlOgENfewFVFqU7O22Yr0OAVoUplFW+ow== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=KaJ5tOMu2Iz5gzsKVuaFww38MOiMUGJF68u52QR23aw=; b=jckRVMr34lu2yZ+lKIXVIOp0z5bwp/AxKiFzuZzbepoOOjJx7wA0beIaBC/892z4l3f4yqxMVrhvcwe4zUqnSOXYKVdtumu8a77q1Cgh/ppLbP+4SQ8B+MoYDdS62Wa3KqO1IPgIc2h8bs+hVCJxTnLa/ukGr5t0xhtUUeHjClzBqyszIGTlBktGuQdSxc6TfChVWUA47blAGsHcaBZFT/o/IdcEZ/XeXgam9Y6p15YRvh7cdah7VvwZwiiToTWMofkldRS6/M7KB0Jqj4NzUFp76ah6nwX5Ay/KJ+xa2JXq4DAsNcwzlddwpwYe/WprAtOIrjtvdVlkEoRwYeezCQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=lwn.net smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=KaJ5tOMu2Iz5gzsKVuaFww38MOiMUGJF68u52QR23aw=; b=dJ64fnocWkYLkqoO7oVLtpgnSDgMaVxfmek/JWURNEQefzyO0PIacMemcRaPu6ddM+GHWXPeLa6GMoOZEpa/dliBTkbuOmtmNHBc6YQH8kPjc3suEHBqtmCJedX0O7kCq0jI8m3zg2UGLE3b/t5vCGlrhaUY2pRXGUkIcDf5Rl0= Received: from DM6PR06CA0083.namprd06.prod.outlook.com (2603:10b6:5:336::16) by DS0PR12MB7631.namprd12.prod.outlook.com (2603:10b6:8:11e::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5857.23; Mon, 28 Nov 2022 15:23:25 +0000 Received: from DM6NAM11FT028.eop-nam11.prod.protection.outlook.com (2603:10b6:5:336:cafe::91) by DM6PR06CA0083.outlook.office365.com (2603:10b6:5:336::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5857.23 via Frontend Transport; Mon, 28 Nov 2022 15:23:25 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by DM6NAM11FT028.mail.protection.outlook.com (10.13.173.140) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.5857.18 via Frontend Transport; Mon, 28 Nov 2022 15:23:24 +0000 Received: from SATLEXMB04.amd.com (10.181.40.145) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.34; Mon, 28 Nov 2022 09:23:24 -0600 Received: from iron-maiden.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server id 15.1.2375.34 via Frontend Transport; Mon, 28 Nov 2022 09:23:24 -0600 From: Carlos Bilbao To: CC: , , , Carlos Bilbao Subject: [PATCH v2] docs/sp_SP: Add memory-barriers.txt Spanish translation Date: Mon, 28 Nov 2022 09:23:23 -0600 Message-ID: <20221128152323.4080455-1-carlos.bilbao@amd.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <6b1d95ed-3000-3baf-81ae-7794c9515e3b@amd.com> References: <6b1d95ed-3000-3baf-81ae-7794c9515e3b@amd.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DM6NAM11FT028:EE_|DS0PR12MB7631:EE_ X-MS-Office365-Filtering-Correlation-Id: e0c3349a-46d1-472f-2a99-08dad1547e26 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: DZqUrRUlQD5UxfiNO8aENbuDitslRQb27nN7wExgxYUEehXcom+1nOGq9w5PYF1J+zxDS6q3E+J/JMq6MDBQLfRzb2A4h+LNxlbIeCRzsR4DpxUYnVK8BBfQMzoEmBhmILPZSFoPe9M2/p85fpHY8J+PPkgKlUY69+KXtzm9SAw1hl2gjsl3aZyqXIvZRFPhJvQSwaKV1wmQrK9CYIu9x0D7P3j+++049elkKlIXWUHgiFJRQTOA6x4BpcgO8Iz/4/y7VNQh3OmR0I7menK2HDBrIowQgB53FLUNX0qpJC4PWuTtO01Xv5aoT1CjcnIKtx6OEES7GqTprBjCkZrhV2Q0PsjNZKaEZqadpQlCF+wTO1y9s+pXJ8YGLlUTvaph1WBeTKsrJWhP3J0xvYO/7efBxAb9wA8mBOFsAt4KEsMxmtwMb0zrXgNeNDV9ReMzn1tW60DiVBeToJVSqLKcPVILAUW2wFqa4upJawjhd9kJE7lHcDX45p6uh/RJBcwvBXvNzIsIxWOaL3CdIyI8nrvqjwLPz575e0DFkNEt2h6nX3R/6wXcxgxxbXvW2+BNCCa5eziZeu/dRuekGBB+qU+HOymw/GNCNZbgROmGOy48hBeGTsGSiGI2xAacSYunz9eLJ7YeBhtZqH28e9KvZ2SuwI+P+K6MvF9fAYKj18Lt4H58P5+JENSQQYyB/EHNfXLQCTUYQi9O0QaEhHRoyn0Ln0VctqzmzSgzysHMVfO7w1ln6Uy2eRreosTBqpwiMqbeSbd+PXEA2c5DAEvCZw== X-Forefront-Antispam-Report: CIP:165.204.84.17;CTRY:US;LANG:es;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:SATLEXMB04.amd.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230022)(4636009)(136003)(396003)(346002)(376002)(39860400002)(451199015)(36840700001)(40470700004)(46966006)(2906002)(83380400001)(2616005)(40480700001)(41300700001)(186003)(356005)(81166007)(1076003)(30864003)(478600001)(86362001)(26005)(82740400003)(7696005)(82310400005)(44832011)(66574015)(8676002)(4326008)(8936002)(47076005)(54906003)(36860700001)(6916009)(40460700003)(316002)(36756003)(426003)(5660300002)(336012)(70206006)(70586007)(21314003)(36900700001)(579004)(559001);DIR:OUT;SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 28 Nov 2022 15:23:24.9453 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: e0c3349a-46d1-472f-2a99-08dad1547e26 X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d;Ip=[165.204.84.17];Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: DM6NAM11FT028.eop-nam11.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DS0PR12MB7631 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Translate the following documents into Spanish: - memory-barriers.txt using the wrapper documents system. Signed-off-by: Carlos Bilbao --- Documentation/translations/sp_SP/index.rst | 1 + .../translations/sp_SP/memory-barriers.txt | 3134 +++++++++++++++++ .../sp_SP/wrappers/memory-barriers.rst | 19 + 3 files changed, 3154 insertions(+) create mode 100644 Documentation/translations/sp_SP/memory-barriers.txt create mode 100644 Documentation/translations/sp_SP/wrappers/memory-barrie= rs.rst diff --git a/Documentation/translations/sp_SP/index.rst b/Documentation/tra= nslations/sp_SP/index.rst index 791cbef75902..5c2a2131524b 100644 --- a/Documentation/translations/sp_SP/index.rst +++ b/Documentation/translations/sp_SP/index.rst @@ -78,3 +78,4 @@ Traducciones al espa=C3=B1ol =20 howto process/index + wrappers/memory-barriers diff --git a/Documentation/translations/sp_SP/memory-barriers.txt b/Documen= tation/translations/sp_SP/memory-barriers.txt new file mode 100644 index 000000000000..f62bd797216d --- /dev/null +++ b/Documentation/translations/sp_SP/memory-barriers.txt @@ -0,0 +1,3134 @@ +NOTE: +This is a version of Documentation/memory-barriers.txt translated into +Spanish by Carlos Bilbao . If you find any +difference between this document and the original file or a problem with +the translation, please contact the maintainer of this file. Please also +note that the purpose of this file is to be easier to read for non English +(read: Spanish) speakers and is not intended as a fork. So if you have any +comments or updates for this file please update the original English file +first. The English version is definitive, and readers should look there if +they have any doubt. + + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + BARRERAS DE MEMORIA EN EL KERNEL LINUX + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Documento original: David Howells + Paul E. McKenney + Will Deacon + Peter Zijlstra + +Traducido por: Carlos Bilbao +Nota: Si tiene alguna duda sobre la exactitud del contenido de esta +traducci=C3=B3n, la =C3=BAnica referencia v=C3=A1lida es la documentaci=C3= =B3n oficial en +ingl=C3=A9s. + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +ADVERTENCIA +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Este documento no es una especificaci=C3=B3n; es intencionalmente (por mot= ivos +de brevedad) y sin querer (por ser humanos) incompleta. Este documento +pretende ser una gu=C3=ADa para usar las diversas barreras de memoria +proporcionadas por Linux, pero ante cualquier duda (y hay muchas) por favor +pregunte. Algunas dudas pueden ser resueltas refiri=C3=A9ndose al modelo de +consistencia de memoria formal y documentaci=C3=B3n en tools/memory-model/= . Sin +embargo, incluso este modelo debe ser visto como la opini=C3=B3n colectiva= de +sus maintainers en lugar de que como un or=C3=A1culo infalible. + +De nuevo, este documento no es una especificaci=C3=B3n de lo que Linux esp= era +del hardware. + +El prop=C3=B3sito de este documento es doble: + + (1) especificar la funcionalidad m=C3=ADnima en la que se puede confiar p= ara + cualquier barrera en concreto, y + + (2) proporcionar una gu=C3=ADa sobre c=C3=B3mo utilizar las barreras disp= onibles. + +Tenga en cuenta que una arquitectura puede proporcionar m=C3=A1s que el +requisito m=C3=ADnimo para cualquier barrera en particular, pero si la +arquitectura proporciona menos de eso, dicha arquitectura es incorrecta. + +Tenga en cuenta tambi=C3=A9n que es posible que una barrera no valga (sea = no-op) +para alguna arquitectura porque por la forma en que funcione dicha +arquitectura, la barrera expl=C3=ADcita resulte innecesaria en ese caso. + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +CONTENIDOS +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + (*) Modelo abstracto de acceso a memoria. + + - Operaciones del dispositivo. + - Garant=C3=ADas. + + (*) =C2=BFQu=C3=A9 son las barreras de memoria? + + - Variedades de barrera de memoria. + - =C2=BFQu=C3=A9 no se puede asumir sobre las barreras de memoria? + - Barreras de direcci=C3=B3n-dependencia (hist=C3=B3ricas). + - Dependencias de control. + - Emparejamiento de barreras smp. + - Ejemplos de secuencias de barrera de memoria. + - Barreras de memoria de lectura frente a especulaci=C3=B3n de carga. + - Atomicidad multicopia. + + (*) Barreras expl=C3=ADcitas del kernel. + + - Barrera del compilador. + - Barreras de memoria de la CPU. + + (*) Barreras de memoria impl=C3=ADcitas del kernel. + + - Funciones de adquisici=C3=B3n de cerrojo. + - Funciones de desactivaci=C3=B3n de interrupciones. + - Funciones de dormir y despertar. + - Funciones varias. + + (*) Efectos de barrera adquiriendo intra-CPU. + + - Adquisici=C3=B3n vs accesos a memoria. + + (*) =C2=BFD=C3=B3nde se necesitan barreras de memoria? + + - Interacci=C3=B3n entre procesadores. + - Operaciones at=C3=B3micas. + - Acceso a dispositivos. + - Interrupciones. + + (*) Efectos de barrera de E/S del kernel. + + (*) Modelo de orden m=C3=ADnimo de ejecuci=C3=B3n asumido. + + (*) Efectos de la memoria cach=C3=A9 de la CPU. + + - Coherencia de cach=C3=A9. + - Coherencia de cach=C3=A9 frente a DMA. + - Coherencia de cach=C3=A9 frente a MMIO. + + (*) Cosas que hacen las CPU. + + - Y luego est=C3=A1 el Alfa. + - Guests de m=C3=A1quinas virtuales. + + (*) Ejemplos de usos. + + - Buffers circulares. + + (*) Referencias. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +MODELO ABSTRACTO DE ACCESO A MEMORIA +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Considere el siguiente modelo abstracto del sistema: + + : : + : : + : : + +-------+ : +--------+ : +-------+ + | | : | | : | | + | | : | | : | | + | CPU 1 |<----->| Memoria|<----->| CPU 2 | + | | : | | : | | + | | : | | : | | + +-------+ : +--------+ : +-------+ + ^ : ^ : ^ + | : | : | + | : | : | + | : v : | + | : +--------+ : | + | : | | : | + | : | Disposi| : | + +---------->| tivo |<----------+ + : | | : + : | | : + : +--------+ : + : : + +Cada CPU ejecuta un programa que genera operaciones de acceso a la memoria. +En la CPU abstracta, el orden de las operaciones de memoria es muy +relajado, y una CPU en realidad puede realizar las operaciones de memoria +en el orden que desee, siempre que la causalidad del programa parezca +mantenerse. De manera similar, el compilador tambi=C3=A9n puede organizar = las +instrucciones que emite en el orden que quiera, siempre que no afecte al +funcionamiento aparente del programa. + +Entonces, en el diagrama anterior, los efectos de las operaciones de +memoria realizadas por un CPU son percibidos por el resto del sistema a +medida que las operaciones cruzan la interfaz entre la CPU y el resto del +sistema (las l=C3=ADneas discontinuas a puntos). + +Por ejemplo, considere la siguiente secuencia de eventos: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + { A =3D=3D 1; B =3D=3D 2 } + A =3D 3; x =3D B; + B =3D 4; y =3D A; + +El conjunto de accesos visto por el sistema de memoria en el medio se puede +organizar en 24 combinaciones diferentes (donde LOAD es cargar y STORE es +guardar): + +STORE A=3D3, STORE B=3D4, y=3DLOAD A->3, x=3DLOAD B->4 +STORE A=3D3, STORE B=3D4, x=3DLOAD B->4, y=3DLOAD A->3 +STORE A=3D3, y=3DLOAD A->3, STORE B=3D4, x=3DLOAD B->4 +STORE A=3D3, y=3DLOAD A->3, x=3DLOAD B->2, STORE B=3D4 +STORE A=3D3, x=3DLOAD B->2, STORE B=3D4, y=3DLOAD A->3 +STORE A=3D3, x=3DLOAD B->2, y=3DLOAD A->3, STORE B=3D4 +STORE B=3D4, STORE A=3D3, y=3DLOAD A->3, x=3DLOAD B->4 +STORE B=3D4, ... +... + +y por lo tanto puede resultar en cuatro combinaciones diferentes de +valores: + +x =3D=3D 2, y =3D=3D 1 +x =3D=3D 2, y =3D=3D 3 +x =3D=3D 4, y =3D=3D 1 +x =3D=3D 4, y =3D=3D 3 + +Adem=C3=A1s, los stores asignados por una CPU al sistema de memoria pueden= no +ser percibidos por los loads realizados por otra CPU en el mismo orden en +que fueron realizados. + +Como otro ejemplo, considere esta secuencia de eventos: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + { A =3D=3D 1, B =3D=3D 2, C =3D=3D 3, P =3D=3D &A, Q =3D=3D &C } + B =3D 4; Q =3D P; + P =3D &B; D =3D *Q; + +Aqu=C3=AD hay una dependencia obvia de la direcci=C3=B3n, ya que el valor = cargado en +D depende en la direcci=C3=B3n recuperada de P por la CPU 2. Al final de la +secuencia, cualquiera de los siguientes resultados son posibles: + + (Q =3D=3D &A) y (D =3D=3D 1) + (Q =3D=3D &B) y (D =3D=3D 2) + (Q =3D=3D &B) y (D =3D=3D 4) + +Tenga en cuenta que la CPU 2 nunca intentar=C3=A1 cargar C en D porque la = CPU +cargar=C3=A1 P en Q antes de emitir la carga de *Q. + +OPERACIONES DEL DISPOSITIVO +--------------------------- + +Algunos dispositivos presentan sus interfaces de control como colecciones +de ubicaciones de memoria, pero el orden en que se accede a los registros +de control es muy importante. Por ejemplo, imagine una tarjeta ethernet con +un conjunto de registros a los que se accede a trav=C3=A9s de un registro = de +puerto de direcci=C3=B3n (A) y un registro de datos del puerto (D). Para l= eer el +registro interno 5, el siguiente c=C3=B3digo podr=C3=ADa entonces ser usad= o: + + *A =3D 5; + x =3D *D; + +pero esto podr=C3=ADa aparecer como cualquiera de las siguientes dos secue= ncias: + + STORE *A =3D 5, x =3D LOAD *D + x =3D LOAD *D, STORE *A =3D 5 + +el segundo de las cuales casi con certeza resultar=C3=A1 en mal funcionami= ento, +ya que se estableci=C3=B3 la direcci=C3=B3n _despu=C3=A9s_ de intentar lee= r el registro. + + +GARANT=C3=8DAS +--------- + +Hay algunas garant=C3=ADas m=C3=ADnimas que se pueden esperar de una CPU: + + (*) En cualquier CPU dada, los accesos a la memoria dependiente se + emitir=C3=A1n en orden, con respeto a s=C3=AD mismo. Esto significa q= ue para: + + Q =3D READ_ONCE(P); D =3D READ_ONCE(*Q); + + donde READ_ONCE() es LEER_UNA_VEZ(), la CPU emitir=C3=A1 las siguient= es + operaciones de memoria: + + Q =3D LOAD P, D =3D LOAD *Q + + y siempre en ese orden. Sin embargo, en DEC Alpha, READ_ONCE() tambi= =C3=A9n + emite una instrucci=C3=B3n de barrera de memoria, de modo que una CPU= DEC + Alpha, sin embargo emite las siguientes operaciones de memoria: + + Q =3D LOAD P, MEMORY_BARRIER, D =3D LOAD *Q, MEMORY_BARRIER + + Ya sea en DEC Alpha o no, READ_ONCE() tambi=C3=A9n evita que el compi= lador + haga cosas inapropiadas. + + (*) Los loads y stores superpuestos dentro de una CPU en particular + parecer=C3=A1n ser ordenados dentro de esa CPU. Esto significa que pa= ra: + + a =3D READ_ONCE(*X); WRITE_ONCE(*X, b); + + donde WRITE_ONCE() es ESCRIBIR_UNA_VEZ(), la CPU solo emitir=C3=A1 la + siguiente secuencia de operaciones de memoria: + + a =3D LOAD *X, STORE *X =3D b + + Y para: + + WRITE_ONCE(*X, c); d =3D READ_ONCE(*X); + + la CPU solo emitir=C3=A1: + + STORE *X =3D c, d =3D LOAD *X + + (Los loads y stores se superponen si est=C3=A1n destinados a piezas + superpuestas de memoria). + +Y hay una serie de cosas que _deben_ o _no_ deben asumirse: + + (*) _No_debe_ asumirse que el compilador har=C3=A1 lo que usted quiera + con referencias de memoria que no est=C3=A1n protegidas por READ_ONCE= () y + WRITE ONCE(). Sin ellos, el compilador tiene derecho a hacer todo tipo + de transformaciones "creativas", que se tratan en la secci=C3=B3n BAR= RERA + DEL COMPILADOR. + + (*) _No_debe_ suponerse que se emitir=C3=A1n loads y stores independien= tes + en el orden dado. Esto significa que para: + + X =3D *A; Y =3D *B; *D =3D Z; + + podemos obtener cualquiera de las siguientes secuencias: + + X =3D LOAD *A, Y =3D LOAD *B, STORE *D =3D Z + X =3D LOAD *A, STORE *D =3D Z, Y =3D LOAD *B + Y =3D LOAD *B, X =3D LOAD *A, STORE *D =3D Z + Y =3D LOAD *B, STORE *D =3D Z, X =3D LOAD *A + STORE *D =3D Z, X =3D LOAD *A, Y =3D LOAD *B + STORE *D =3D Z, Y =3D LOAD *B, X =3D LOAD *A + + (*) Se _debe_ suponer que los accesos de memoria superpuestos pueden + fusionarse o ser descartados. Esto significa que para: + + X =3D *A; Y =3D *(A + 4); + + podemos obtener cualquiera de las siguientes secuencias: + +X =3D LOAD *A; Y =3D LOAD *(A + 4); +Y =3D LOAD *(A + 4); X =3D LOAD *A; +{X, Y} =3D LOAD {*A, *(A + 4) }; + + Y para: + +*A =3D X; *(A + 4) =3D Y; + + podemos obtener cualquiera de: + +STORE *A =3D X; STORE *(A + 4) =3D Y; +STORE *(A + 4) =3D Y; STORE *A =3D X; +STORE {*A, *(A + 4) } =3D {X, Y}; + +Y hay anti-garant=C3=ADas: + +(*) Estas garant=C3=ADas no se aplican a los campos de bits, porque los + compiladores a menudo generan c=C3=B3digo para modificarlos usando + secuencias de lectura-modificaci=C3=B3n-escritura no at=C3=B3mica. No = intente + utilizar campos de bits para sincronizar algoritmos paralelos. + +(*) Incluso en los casos en que los campos de bits est=C3=A1n protegidos p= or + cerrojos (o "cerrojos", o "locks"), todos los componentes en un campo + de bits dado deben estar protegidos por un candado. Si dos campos en un + campo de bits dado est=C3=A1n protegidos por diferentes locks, las + secuencias de lectura-modificaci=C3=B3n-escritura no at=C3=B3micas del= lock + pueden causar una actualizaci=C3=B3n a una campo para corromper el val= or de + un campo adyacente. + +(*) Estas garant=C3=ADas se aplican solo a escalares correctamente alinead= os y + dimensionados. De "tama=C3=B1o adecuado" significa actualmente variabl= es que + son del mismo tama=C3=B1o que "char", "short", "int" y "long". + "Adecuadamente alineado" significa la alineaci=C3=B3n natural, por lo = tanto, + no hay restricciones para "char", alineaci=C3=B3n de dos bytes para "s= hort", + alineaci=C3=B3n de cuatro bytes para "int", y alineaci=C3=B3n de cuatr= o u ocho + bytes para "long", en sistemas de 32 y 64 bits, respectivamente. Tenga + en cuenta que estos garant=C3=ADas se introdujeron en el est=C3=A1ndar= C11, as=C3=AD + que tenga cuidado cuando utilice compiladores anteriores a C11 (por + ejemplo, gcc 4.6). La parte de la norma que contiene esta garant=C3=AD= a es + la Secci=C3=B3n 3.14, que define "ubicaci=C3=B3n de memoria" de la sig= uiente + manera: + + ubicaci=C3=B3n de memoria + ya sea un objeto de tipo escalar, o una secuencia m=C3=A1xima + de campos de bits adyacentes, todos con ancho distinto de cero + + NOTE 1: Dos hilos de ejecuci=C3=B3n pueden actualizar y acceder + ubicaciones de memoria separadas sin interferir entre + ellos. + + NOTE 2: Un campo de bits y un miembro adyacente que no es un campo de + bits est=C3=A1n en ubicaciones de memoria separadas. Lo mismo sucede con + dos campos de bits, si uno se declara dentro de un declaraci=C3=B3n de + estructura anidada y el otro no, o si las dos est=C3=A1n separados por u= na + declaraci=C3=B3n de campo de bits de longitud cero, o si est=C3=A1n sepa= rados por + un miembro no declarado como campo de bits. No es seguro actualizar + simult=C3=A1neamente dos campos de bits en la misma estructura si entre + todos los miembros declarados tambi=C3=A9n hay campos de bits, sin impor= tar + cu=C3=A1l resulta ser el tama=C3=B1o de estos campos de bits intermedios. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +=C2=BFQU=C3=89 SON LAS BARRERAS DE MEMORIA? +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Como se puede leer arriba, las operaciones independientes de memoria se +realizan de manera efectiva en orden aleatorio, pero esto puede ser un +problema para la interacci=C3=B3n CPU-CPU y para la E/S ("I/O"). Lo que se +requiere es alguna forma de intervenir para instruir al compilador y al +CPU para restringir el orden. + +Las barreras de memoria son este tipo de intervenciones. Imponen una +percepci=C3=B3n de orden parcial, sobre las operaciones de memoria a ambos= lados +de la barrera. + +Tal cumplimiento es importante porque las CPUs y otros dispositivos en un +sistema pueden usar una variedad de trucos para mejorar el rendimiento, +incluido el reordenamiento, diferimiento y combinaci=C3=B3n de operaciones= de +memoria; cargas especulativas; predicci=C3=B3n de "branches" especulativos= y +varios tipos de almacenamiento en cach=C3=A9. Las barreras de memoria se +utilizan para anular o suprimir estos trucos, permitiendo que el c=C3=B3di= go +controle sensatamente la interacci=C3=B3n de m=C3=BAltiples CPU y/o dispos= itivos. + + +VARIEDADES DE BARRERA DE MEMORIA +--------------------------------- + +Las barreras de memoria vienen en cuatro variedades b=C3=A1sicas: + + (1) Barreras de memoria al escribir o almacenar (Write or store memory + barriers). + + Una barrera de memoria de escritura garantiza que todas las + operaciones de STORE especificadas antes de que la barrera aparezca + suceden antes de todas las operaciones STORE especificadas despu=C3= =A9s + de la barrera, con respecto a los otros componentes del sistema. + + Una barrera de escritura es un orden parcial solo en los stores; No + es requerido que tenga ning=C3=BAn efecto sobre los loads. + + Se puede considerar que una CPU env=C3=ADa una secuencia de operacion= es de + store al sistema de memoria a medida que pasa el tiempo. Todos los + stores _antes_ de una barrera de escritura ocurrir=C3=A1n _antes_ de = todos + los stores despu=C3=A9s de la barrera de escritura. + + [!] Tenga en cuenta que las barreras de escritura normalmente deben + combinarse con read o barreras de address-dependency barriers + (dependencia de direcci=C3=B3n); consulte la subsecci=C3=B3n + "Emparejamiento de barreras smp". + + + (2) Barrera de dependencia de direcci=C3=B3n (hist=C3=B3rico). + + Una barrera de dependencia de direcci=C3=B3n es una forma m=C3=A1s d= =C3=A9bil de + barrera de lectura. En el caso de que se realicen dos loads de manera + que la segunda dependa del resultado de la primera (por ejemplo: el + primer load recupera la direcci=C3=B3n a la que se dirigir=C3=A1 el s= egundo + load), una barrera de dependencia de direcci=C3=B3n ser=C3=ADa necesa= ria para + asegurarse de que el objetivo de la segunda carga est=C3=A9 actualiza= do + despu=C3=A9s de acceder a la direcci=C3=B3n obtenida por la primera c= arga. + + Una barrera de dependencia de direcciones es una ordenaci=C3=B3n parc= ial en + laods de direcciones interdependientes; no se requiere que tenga + ning=C3=BAn efecto en los stores, ya sean cargas de memoria o cargas + de memoria superpuestas. + + Como se mencion=C3=B3 en (1), las otras CPU en el sistema pueden vers= e como + secuencias de stores en el sistema de memoria que la considerada CPU + puede percibir. Una barrera de dependencia de direcci=C3=B3n emitida = por + la CPU en cuesti=C3=B3n garantiza que para cualquier carga que la pre= ceda, + si esa carga toca alguna secuencia de stores de otra CPU, entonces + en el momento en que la barrera se complete, los efectos de todos los + stores antes del cambio del load ser=C3=A1n perceptibles por cualquier + carga emitida despu=C3=A9s la barrera de la dependencia de la direcci= =C3=B3n. + + Consulte la subsecci=C3=B3n "Ejemplos de secuencias de barrera de mem= oria" + para ver los diagramas mostrando las restricciones de orden. + + [!] Tenga en cuenta que la primera carga realmente tiene que tener una + dependencia de _direcci=C3=B3n_ y no es una dependencia de control. S= i la + direcci=C3=B3n para la segunda carga depende de la primera carga, per= o la + dependencia es a trav=C3=A9s de un condicional en lugar de -en realid= ad- + cargando la direcci=C3=B3n en s=C3=AD, entonces es una dependencia de= _control_ + y se requiere una barrera de lectura completa o superior. Consulte la + subsecci=C3=B3n "Dependencias de control" para m=C3=A1s informaci=C3= =B3n. + + [!] Tenga en cuenta que las barreras de dependencia de direcci=C3=B3n + normalmente deben combinarse con barreras de escritura; consulte la + subsecci=C3=B3n "Emparejamiento de barreras smp". + + [!] Desde el kernel v5.9, se elimin=C3=B3 la API del kernel para barr= eras + de memoria de direcciones expl=C3=ADcitas. Hoy en d=C3=ADa, las APIs = para marcar + cargas de variables compartidas, como READ_ONCE() y rcu_dereference(), + proporcionan barreras de dependencia de direcci=C3=B3n impl=C3=ADcita= s. + + (3) Barreras de memoria al leer o cargar (Read or load memory + barriers). + + Una barrera de lectura es una barrera de dependencia de direcciones, + m=C3=A1s una garant=C3=ADa de que todas las operaciones de LOAD espec= ificadas + antes de la barrera parecer=C3=A1n ocurrir antes de todas las operaci= ones + de LOAD especificadas despu=C3=A9s de la barrera con respecto a los d= em=C3=A1s + componentes del sistema. + + Una barrera de lectura es un orden parcial solo en cargas; no es + necesario que tenga ning=C3=BAn efecto en los stores. + + Las barreras de memoria de lectura implican barreras de dependencia de + direcciones, y por tanto puede sustituirlas por estas. + + [!] Tenga en mente que las barreras de lectura normalmente deben + combinarse con barreras de escritura; consulte la subsecci=C3=B3n + "Emparejamiento de barreras smp". + + (4) Barreras de memoria generales + + Una barrera de memoria general proporciona la garant=C3=ADa de que to= das + las operaciones LOAD y STORE especificadas antes de que la barrera + aparezca suceden antes de que todas las operaciones LOAD y STORE + especificadas despu=C3=A9s de la barrera con respecto a los dem=C3=A1s + componentes del sistema. + + Una barrera de memoria general es un orden parcial tanto en + operaciones de carga como de almacenamiento. + + Las barreras de memoria generales implican barreras de memoria tanto + de lectura como de escritura, de modo que pueden sustituir a + cualquiera. + +Y un par de variedades impl=C3=ADcitas: + + (5) ACQUIRE (de adquisici=C3=B3n). + + Esto act=C3=BAa como una barrera permeable unidireccional. Garantiza = que + toda las operaciones de memoria despu=C3=A9s de la operaci=C3=B3n ACQ= UIRE + parezcan suceder despu=C3=A9s de la ACQUIRE con respecto a los dem=C3= =A1s + componentes del sistema. Las operaciones ACQUIRE incluyen operaciones + LOCK y smp_load_acquire(), y operaciones smp_cond_load_acquire(). + + Las operaciones de memoria que ocurren antes de una operaci=C3=B3n AC= QUIRE + pueden parecer suceder despu=C3=A9s de que se complete. + + Una operaci=C3=B3n ACQUIRE casi siempre debe estar emparejada con una + operaci=C3=B3n RELEASE (de liberaci=C3=B3n). + + + (6) Operaciones RELEASE (de liberaci=C3=B3n). + + Esto tambi=C3=A9n act=C3=BAa como una barrera permeable unidirecciona= l. + Garantiza que todas las operaciones de memoria antes de la operaci=C3= =B3n + RELEASE parecer=C3=A1n ocurrir antes de la operaci=C3=B3n RELEASE con= respecto a + los dem=C3=A1s componentes del sistema. Las operaciones de RELEASE in= cluyen + operaciones de UNLOCK y operaciones smp_store_release(). + + Las operaciones de memoria que ocurren despu=C3=A9s de una operaci=C3= =B3n + RELEASE pueden parecer suceder antes de que se complete. + + El uso de las operaciones ACQUIRE y RELEASE generalmente excluye la + necesidad de otros tipos de barrera de memoria. Adem=C3=A1s, un par + RELEASE+ACQUIRE NO garantiza actuar como una barrera de memoria + completa. Sin embargo, despu=C3=A9s de un ACQUIRE de una variable dad= a, + todos los accesos a la memoria que preceden a cualquier anterior + RELEASE en esa misma variable est=C3=A1n garantizados como visibles. = En + otras palabras, dentro de la secci=C3=B3n cr=C3=ADtica de una variabl= e dada, + todos los accesos de todas las secciones cr=C3=ADticas anteriores par= a esa + variable habr=C3=A1n terminado de forma garantizada. + + Esto significa que ACQUIRE act=C3=BAa como una operaci=C3=B3n m=C3=AD= nima de + "adquisici=C3=B3n" y RELEASE act=C3=BAa como una operaci=C3=B3n m=C3= =ADnima de + "liberaci=C3=B3n". + +Un subconjunto de las operaciones at=C3=B3micas descritas en atomic_t.txt +contiene variantes de ACQUIRE y RELEASE, adem=C3=A1s de definiciones +completamente ordenadas o relajadas (sin barrera sem=C3=A1ntica). Para +composiciones at=C3=B3micas que realizan tanto un load como store, la sem= =C3=A1ntica +ACQUIRE se aplica solo a la carga y la sem=C3=A1ntica RELEASE se aplica s= =C3=B3lo a +la parte de la operaci=C3=B3n del store. + +Las barreras de memoria solo son necesarias cuando existe la posibilidad de +interacci=C3=B3n entre dos CPU o entre una CPU y un dispositivo. Si se pue= de +garantizar que no habr=C3=A1 tal interacci=C3=B3n en ninguna pieza de c=C3= =B3digo en +particular, entonces las barreras de memoria son innecesarias en ese +fragmento de c=C3=B3digo. + +Tenga en cuenta que estas son las garant=C3=ADas _m=C3=ADnimas_. Diferentes +arquitecturas pueden proporcionar garant=C3=ADas m=C3=A1s sustanciales, pe= ro no se +puede confiar en estas fuera de esa arquitectura en espec=C3=ADfico. + + +=C2=BFQU=C3=89 NO SE PUEDE ASUMIR SOBRE LAS BARRERAS DE LA MEMORIA? +--------------------------------------------------------- + +Hay ciertas cosas que las barreras de memoria del kernel Linux no +garantizan: + + (*) No hay garant=C3=ADa de que ninguno de los accesos a la memoria + especificados antes de una barrera de memoria estar=C3=A1 _completo_ = al + completarse una instrucci=C3=B3n de barrera de memoria; se puede cons= iderar + que la barrera dibuja una l=C3=ADnea en la cola de acceso del CPU que= no + pueden cruzar los accesos del tipo correspondiente. + + (*) No hay garant=C3=ADa de que la emisi=C3=B3n de una barrera de memoria= en una CPU + tenga cualquier efecto directo en otra CPU o cualquier otro hardware + en el sistema. El efecto indirecto ser=C3=A1 el orden en que la segun= da CPU + ve los efectos de los primeros accesos que ocurren de la CPU, pero lea + el siguiente argumento: + + (*) No hay garant=C3=ADa de que una CPU vea el orden correcto de los efec= tos + de los accesos de una segunda CPU, incluso _si_ la segunda CPU usa una + barrera de memoria, a menos que la primera CPU _tambi=C3=A9n_ use una + barrera de memoria coincidente (vea el subapartado "Emparejamiento de + barrera SMP"). + + (*) No hay garant=C3=ADa de que alguna pieza intermedia fuera del hardwar= e[*] + del CPU no reordenar=C3=A1 los accesos a la memoria. Los mecanismos de + coherencia de cach=C3=A9 del CPU deben propagar los efectos indirecto= s de + una barrera de memoria entre las CPU, pero es posible que no lo hagan + en orden. + + [*] Para obtener informaci=C3=B3n sobre bus mastering DMA y coherencia, l= ea: + + Documentation/driver-api/pci/pci.rst + Documentation/core-api/dma-api-howto.rst + Documentation/core-api/dma-api.rst + + +BARRERA DE DEPENDENCIA DE DIRECCI=C3=93N (HIST=C3=93RICO) +----------------------------------------------- + +A partir de la versi=C3=B3n 4.15 del kernel Linux, se agreg=C3=B3 un smp_m= b() a +READ_ONCE() para DEC Alpha, lo que significa que las =C3=BAnicas personas = que +necesitan prestar atenci=C3=B3n a esta secci=C3=B3n son aquellas que traba= jan en el +c=C3=B3digo espec=C3=ADfico de la arquitectura DEC Alpha y aquellas que tr= abajan en +READ_ONCE() por dentro. Para aquellos que lo necesitan, y para aquellos que +est=C3=A9n interesados =E2=80=8B=E2=80=8Bdesde un punto de vista hist=C3= =B3rico, aqu=C3=AD est=C3=A1 la historia +de las barreras de dependencia de direcci=C3=B3n. + +[!] Si bien las dependencias de direcciones se observan tanto en carga a +carga como en relaciones de carga a store, las barreras de dependencia de +direcci=C3=B3n no son necesarias para situaciones de carga a store. + +El requisito de las barreras de dependencia de direcci=C3=B3n es un poco s= util, +y no siempre es obvio que sean necesarias. Para ilustrar, considere la +siguiente secuencia de eventos: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D + { A =3D=3D 1, B =3D=3D 2, C =3D=3D 3, P =3D=3D &A, Q =3D=3D &C } + B =3D 4; + + WRITE_ONCE(P, &B); + Q =3D READ_ONCE_OLD(P); + D =3D *Q; + +[!] READ_ONCE_OLD() corresponde a READ_ONCE() del kernel anterior a 4.15, +que no implica una barrera de dependencia de direcciones. + +Hay una clara dependencia de direcci=C3=B3n aqu=C3=AD, y parecer=C3=ADa qu= e al final de +la secuencia, Q debe ser &A o &B, y que: + + (Q =3D=3D &A) implica (D =3D=3D 1) + (Q =3D=3D &B) implica (D =3D=3D 4) + +=C2=A1Pero! La percepci=C3=B3n de la CPU 2 de P puede actualizarse _antes_= de su +percepci=C3=B3n de B, por lo tanto dando lugar a la siguiente situaci=C3= =B3n: + + (Q =3D=3D &B) y (D =3D=3D 2) ???? + +Si bien esto puede parecer una falla en el mantenimiento de la coherencia +o la causalidad, no lo es, y este comportamiento se puede observar en +ciertas CPU reales (como DEC Alfa). + +Para lidiar con esto, READ_ONCE() proporciona una barrera de dependencia +de direcci=C3=B3n impl=C3=ADcita desde el lanzamiento del kernel v4.15: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D + { A =3D=3D 1, B =3D=3D 2, C =3D=3D 3, P =3D=3D &A, Q =3D=3D &C } + B =3D 4; + + WRITE_ONCE(P, &B); + Q =3D READ_ONCE(P); + + D =3D *Q; + +Esto refuerza la ocurrencia de una de las dos implicaciones, y previene la +tercera posibilidad de surgir. + + +[!] Tenga en cuenta que esta situaci=C3=B3n extremadamente contraria a la +intuici=C3=B3n surge m=C3=A1s f=C3=A1cilmente en m=C3=A1quinas con cach=C3= =A9s divididos, de modo +que, por ejemplo, un banco de cach=C3=A9 procesa l=C3=ADneas de cach=C3=A9= pares y el otro +banco procesa l=C3=ADneas impares de cach=C3=A9. El puntero P podr=C3=ADa = almacenarse en +una l=C3=ADnea de cach=C3=A9 impar y la variable B podr=C3=ADa almacenarse= en una l=C3=ADnea de +cach=C3=A9 con n=C3=BAmero par. Entonces, si el banco de n=C3=BAmeros pare= s de la memoria +cach=C3=A9 de la CPU de lectura est=C3=A1 extremadamente ocupado mientras = que el +banco impar est=C3=A1 inactivo, uno podr=C3=ADa ver el nuevo valor del pun= tero P +(&B), pero el antiguo valor de la variable B (2). + + +No se requiere una barrera de dependencia de direcci=C3=B3n para ordenar +escrituras dependientes porque las CPU que admite el kernel Linux no +escriben hasta que est=C3=A1n seguros (1) de que la escritura realmente +suceder=C3=A1, (2) de la ubicaci=C3=B3n de la escritura, y (3) del valor a= escribir. +Pero, por favor, lea atentamente la secci=C3=B3n "DEPENDENCIAS DEL CONTROL= " y el +archivo Documentation/RCU/rcu_dereference.rst: el compilador puede romperse +y romper dependencias en muchas formas altamente creativas. + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D + { A =3D=3D 1, B =3D=3D 2, C =3D 3, P =3D=3D &A, Q =3D=3D &C } + B =3D 4; + + WRITE_ONCE(P, &B); + Q =3D READ_ONCE_OLD(P); + WRITE_ONCE(*Q, 5); + +Por lo tanto, no se requiere ninguna barrera de dependencia de direcciones +para ordenar la lectura en Q con el load en *Q. En otras palabras, este +resultado est=C3=A1 prohibido, incluso sin una barrera de dependencia de +direcci=C3=B3n impl=C3=ADcita del READ_ONCE() moderno: + + (Q =3D=3D &B) && (B =3D=3D 4) + +Tenga en cuenta que este patr=C3=B3n debe ser raro. Despu=C3=A9s de todo, = el objetivo +del orden de dependencia es -prevenir- escrituras en la estructura de +datos, junto con los costosos errores de cach=C3=A9 asociados con tales +escrituras. Este patr=C3=B3n se puede utilizar para registrar raras condic= iones +de error y similares, y el orden natural de las CPUs evita que se pierdan +tales registros. + + +Tenga en cuenta que el orden proporcionado por una dependencia de direcci= =C3=B3n +es local para la CPU que lo contiene. Lea la secci=C3=B3n sobre "Atomicidad +multicopia" para m=C3=A1s informaci=C3=B3n. + + +La barrera de dependencia de direcci=C3=B3n es muy importante para el sist= ema +RCU, por ejemplo. Vea rcu_assign_pointer() y rcu_dereference() en +include/linux/rcupdate.h. Esto permite que el objetivo actual de un puntero +RCU sea reemplazado con un nuevo objetivo modificado, sin que el objetivo +del reemplazo parezca estar inicializado de manera incompleta. + +Consulte tambi=C3=A9n la subsecci=C3=B3n sobre "Coherencia de cach=C3=A9" = para obtener un +ejemplo m=C3=A1s completo. + +DEPENDENCIAS DE CONTROL +----------------------- + +Las dependencias de control pueden ser un poco complicadas porque los +compiladores actuales no las entienden. El prop=C3=B3sito de esta secci=C3= =B3n es +ayudarle a prevenir que la ignorancia del compilador rompa su c=C3=B3digo. + +Una dependencia de control load-load (de carga a carga) requiere una +barrera de memoria de lectura completa, no simplemente una barrera +(impl=C3=ADcita) de dependencia de direcciones para que funcione correctam= ente. +Considere el siguiente fragmento de c=C3=B3digo: + + q =3D READ_ONCE(a); + + if (q) { + /* BUG: No hay dependencia de direcci=C3=B3n!!! */ + p =3D READ_ONCE(b); + } + +Esto no tendr=C3=A1 el efecto deseado porque no hay una dependencia de dir= ecci=C3=B3n +real, sino m=C3=A1s bien una dependencia de control que la CPU puede +cortocircuitar al intentar predecir el resultado por adelantado, para que +otras CPU vean la carga de b como si hubiera ocurrido antes que la carga de +a. En cuyo caso lo que realmente se requiere es: + + q =3D READ_ONCE(a); + if (q) { + + p =3D READ_ONCE(b); + } + +Sin embargo, los stores no se especulan. Esto significa que ordenar -es- +provisto para dependencias de control de load-store, como en el siguiente +ejemplo: + + q =3D READ_ONCE(a); + if (q) { + WRITE_ONCE(b, 1); + } + +Las dependencias de control se emparejan normalmente con otros tipos de +barreras. Dicho esto, tenga en cuenta que ni READ_ONCE() ni WRITE_ONCE() +son opcionales! Sin READ_ONCE(), el compilador podr=C3=ADa combinar la car= ga de +'a' con otras cargas de 'a'. Sin WRITE_ONCE(), el compilador podr=C3=ADa +combinar el store de 'b' con otros stores de 'b'. Cualquiera de estos casos +puede dar lugar a efectos en el orden muy contrarios a la intuici=C3=B3n. + +Peor a=C3=BAn, si el compilador puede probar (decir) que el valor de la +variable 'a' siempre es distinta de cero, estar=C3=ADa dentro de sus derec= hos +para optimizar el ejemplo original eliminando la declaraci=C3=B3n "if", co= mo: + + q =3D a; + b =3D 1; /* BUG: Compilador y CPU pueden ambos reordernar!!! */ + +As=C3=AD que no deje de lado READ_ONCE(). + +Es tentador tratar de hacer cumplir el orden en stores id=C3=A9nticos en a= mbos +caminos del "if" de la siguiente manera: + + q =3D READ_ONCE(a); + if (q) { + barrier(); + WRITE_ONCE(b, 1); + hacer_algo(); + } else { + barrier(); + WRITE_ONCE(b, 1); + hacer_otra_cosa(); + } + +Desafortunadamente, los compiladores actuales transformar=C3=A1n esto de la +siguiente manera en casos de alto nivel de optimizaci=C3=B3n: + + q =3D READ_ONCE(a); + barrier(); + WRITE_ONCE(b, 1); /* BUG: No hay orden en load de a!!! */ + if (q) { + /* WRITE_ONCE(b, 1); -- movido arriba, BUG!!! */ + hacer_algo(); + } else { + /* WRITE_ONCE(b, 1); -- movido arriba, BUG!!! */ + hacer_otra_cosa(); + } + +Ahora no hay condicional entre la carga de 'a' y el store de 'b', lo que +significa que la CPU est=C3=A1 en su derecho de reordenarlos: El condicion= al es +absolutamente necesario y debe estar presente en el c=C3=B3digo ensamblador +incluso despu=C3=A9s de que se hayan aplicado todas las optimizaciones del +compilador. Por lo tanto, si necesita ordenar en este ejemplo, necesita +expl=C3=ADcitamente barreras de memoria, por ejemplo, smp_store_release(): + + + q =3D READ_ONCE(a); + if (q) { + smp_store_release(&b, 1); + hacer_algo(); + } else { + smp_store_release(&b, 1); + hacer_otra_cosa(); + } + +Por el contrario, sin barreras de memoria expl=C3=ADcita, el control de un= if +con dos opciones est=C3=A1 garantizado solo cuando los stores difieren, por +ejemplo: + + q =3D READ_ONCE(a); + if (q) { + WRITE_ONCE(b, 1); + hacer_algo(); + } else { + WRITE_ONCE(b, 2); + hacer_otra_cosa(); + } + +A=C3=BAn se requiere el inicial READ_ONCE() para evitar que el compilador = toque +el valor de 'a'. + +Adem=C3=A1s, debe tener cuidado con lo que hace con la variable local 'q',= de lo +contrario, el compilador podr=C3=ADa adivinar el valor y volver a eliminar= el +necesario condicional. Por ejemplo: + + q =3D READ_ONCE(a); + if (q % MAX) { + WRITE_ONCE(b, 1); + hacer_algo(); + } else { + WRITE_ONCE(b, 2); + hacer_otra_cosa(); + } + +Si MAX se define como 1, entonces el compilador sabe que (q % MAX) es igual +a cero, en cuyo caso el compilador tiene derecho a transformar el c=C3=B3d= igo +anterior en el siguiente: + + q =3D READ_ONCE(a); + WRITE_ONCE(b, 2); + hacer_otra_cosa(); + +Dada esta transformaci=C3=B3n, la CPU no est=C3=A1 obligada a respetar el = orden entre +la carga de la variable 'a' y el store de la variable 'b'. Es tentador +agregar una barrier(), pero esto no ayuda. El condicional se ha ido, y la +barrera no lo traer=C3=A1 de vuelta. Por lo tanto, si confia en este orden= , debe +asegurarse de que MAX sea mayor que uno, tal vez de la siguiente manera: + + q =3D READ_ONCE(a); + BUILD_BUG_ON(MAX <=3D 1); /* Orden de carga de a con store de b */ + if (q % MAX) { + WRITE_ONCE(b, 1); + hacer_algo(); + } else { + WRITE_ONCE(b, 2); + hacer_otra_cosa(); + } + +Tenga en cuenta una vez m=C3=A1s que los stores de 'b' difieren. Si fueran +id=C3=A9nticos, como se se=C3=B1al=C3=B3 anteriormente, el compilador podr= =C3=ADa sacar ese +store fuera de la declaraci=C3=B3n 'if'. + +Tambi=C3=A9n debe tener cuidado de no confiar demasiado en el cortocircuito +de la evaluaci=C3=B3n booleana. Considere este ejemplo: + + q =3D READ_ONCE(a); + if (q || 1 > 0) + WRITE_ONCE(b, 1); + +Debido a que la primera condici=C3=B3n no puede fallar y la segunda condic= i=C3=B3n es +siempre cierta, el compilador puede transformar este ejemplo de la +siguiente manera, rompiendo la dependencia del control: + + q =3D READ_ONCE(a); + WRITE_ONCE(b, 1); + +Este ejemplo subraya la necesidad de asegurarse de que el compilador no +pueda adivinar su c=C3=B3digo. M=C3=A1s generalmente, aunque READ_ONCE() f= uerza +al compilador para emitir c=C3=B3digo para una carga dada, no fuerza al +compilador para usar los resultados. + +Adem=C3=A1s, las dependencias de control se aplican solo a la cl=C3=A1usul= a then y +la cl=C3=A1usula else de la sentencia if en cuesti=C3=B3n. En particular, = no se +aplica necesariamente al c=C3=B3digo que sigue a la declaraci=C3=B3n if: + + q =3D READ_ONCE(a); + if (q) { + WRITE_ONCE(b, 1); + } else { + WRITE_ONCE(b, 2); + } + WRITE_ONCE(c, 1); /* BUG: No hay orden para la lectura de 'a'. */ + +Es tentador argumentar que, de hecho, existe un orden porque el compilador +no puede reordenar accesos vol=C3=A1tiles y tampoco puede reordenar escrit= uras +en 'b' con la condici=C3=B3n. Desafortunadamente para esta l=C3=ADnea de +razonamiento, el compilador podr=C3=ADa compilar las dos escrituras en 'b'= como +instrucciones de movimiento condicional, como en este fant=C3=A1stico idio= ma +pseudo-ensamblador: + + ld r1,a + cmp r1,$0 + cmov,ne r4,$1 + cmov,eq r4,$2 + st r4,b + st $1,c + +Una CPU d=C3=A9bilmente ordenada no tendr=C3=ADa dependencia de ning=C3=BA= n tipo entre la +carga de 'a' y el store de 'c'. Las dependencias de control se extender=C3= =ADan +solo al par de instrucciones cmov y el store dependiente de ellas. En +resumen, las dependencias de control se aplican solo a los stores en la +cl=C3=A1usula then y la cl=C3=A1usula else de la sentencia if en cuesti=C3= =B3n (incluidas +las funciones invocado por esas dos cl=C3=A1usulas), no al c=C3=B3digo que= sigue a +esa declaraci=C3=B3n if. + + +Tenga muy en cuenta que el orden proporcionado por una dependencia de +control es local a la CPU que lo contiene. Vea el apartado de "Atomicidad +multicopia" para m=C3=A1s informaci=C3=B3n. + + +En resumen: + + (*) Las dependencias de control pueden ordenar cargas anteriores para + stores posteriores. Sin embargo, no garantizan ning=C3=BAn otro tipo= de + orden: No cargas previas contra cargas posteriores, ni + almacenamientos previos y luego nada. Si necesita tales formas de + orden, use smp_rmb(), smp_wmb() o, en el caso de stores anteriores y + cargas posteriores, smp_mb(). + + (*) Si ambos caminos de la declaraci=C3=B3n "if" comienzan con stores + id=C3=A9nticos de la misma variable, entonces esos stores deben ser + ordenados, ya sea precedi=C3=A9ndoles a ambos con smp_mb() o usando + smp_store_release() para realizar el store. Tenga en cuenta que -no- + es suficiente usar barrier() al comienzo de cada caso de la + declaraci=C3=B3n "if" porque, como se muestra en el ejemplo anterior= , la + optimizaci=C3=B3n de los compiladores puede destruir la dependencia = de + control respetando al pie de la letra la ley de barrier(). + + (*) Las dependencias de control requieren al menos un condicional en + tiempo de ejecuci=C3=B3n entre la carga anterior y el almacenamiento + posterior, y este condicional debe implicar la carga previa. Si el + compilador es capaz de optimizar el condicional y quitarlo, tambi=C3= =A9n + habr=C3=A1 optimizado el ordenar. El uso cuidadoso de READ_ONCE() y + WRITE_ONCE() puede ayudar a preservar el necesario condicional. + + (*) Las dependencias de control requieren que el compilador evite + reordenar las dependencia hasta su inexistencia. El uso cuidadoso de + READ_ONCE() o atomic{,64}_read() puede ayudarle a preservar la + dependencia de control. Consulte la secci=C3=B3n BARRERA DEL COMPILA= DOR + para obtener m=C3=A1s informaci=C3=B3n al respecto. + + (*) Las dependencias de control se aplican solo a la cl=C3=A1usula then = y la + cl=C3=A1usula else de la sentencia "if" que contiene la dependencia = de + control, incluyendo cualquier funci=C3=B3n a la que llamen dichas dos + cl=C3=A1usulas. Las dependencias de control no se aplican al c=C3=B3= digo que + sigue a la instrucci=C3=B3n if que contiene la dependencia de contro= l. + + (*) Las dependencias de control se emparejan normalmente con otros tipos + de barreras. + + (*) Las dependencias de control no proporcionan atomicidad multicopia. Si + usted necesita todas las CPU para ver un store dado al mismo tiempo, + emplee smp_mb(). + + (*) Los compiladores no entienden las dependencias de control. Por lo + tanto es su trabajo asegurarse de que no rompan su c=C3=B3digo. + + +EMPAREJAMIENTO DE BARRERAS SMP +------------------------------ + +Cuando se trata de interacciones CPU-CPU, ciertos tipos de barrera de +memoria deben estar siempre emparejados. La falta del apropiado +emparejamiento es casi seguro un error. + +Las barreras generales se emparejan entre s=C3=AD, aunque tambi=C3=A9n se = emparejan +con la mayor=C3=ADa de otro tipo de barreras, aunque sin atomicidad multic= opia. +Una barrera de adquisici=C3=B3n se empareja con una barrera de liberaci=C3= =B3n, pero +ambas tambi=C3=A9n pueden emparejarse con otras barreras, incluidas, por +supuesto, las barreras generales. Una barrera de escritura se empareja con +una barrera de dependencia de direcci=C3=B3n, una dependencia de control, = una +barrera de adquisici=C3=B3n, una barrera de liberaci=C3=B3n, una barrera d= e lectura +o una barrera general. Del mismo modo, una barrera de lectura se empareja +con una de dependencia de control o barrera de dependencia de direcci=C3= =B3n con +una barrera de escritura, una barrera de adquisici=C3=B3n, una barrera de +liberaci=C3=B3n o una barrera general: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D + WRITE_ONCE(a, 1); + + WRITE_ONCE(b, 2); x =3D READ_ONCE(b); + + y =3D READ_ONCE(a); + +O bien: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + a =3D 1; + + WRITE_ONCE(b, &a); x =3D READ_ONCE(b); + + y =3D *x; + +O incluso: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + r1 =3D READ_ONCE(y); + + WRITE_ONCE(x, 1); if (r2 =3D READ_ONCE(x)) { + + WRITE_ONCE(y, 1); + } + + assert(r1 =3D=3D 0 || r2 =3D=3D 0); + +B=C3=A1sicamente, la barrera de lectura siempre tiene que estar ah=C3=AD, = aunque +puede ser del tipo "m=C3=A1s d=C3=A9bil". + +[!] Tenga en cuenta que normalmente se esperar=C3=ADa que los stores antes= de la +barrera de escritura se hagan coincidir con los stores despu=C3=A9s de la +barrera de lectura o la barrera de dependencia de direcci=C3=B3n, y viceve= rsa: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D = =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + WRITE_ONCE(a, 1); }---- --->{ v =3D READ_ONCE(c); + WRITE_ONCE(b, 2); } \ / { w =3D READ_ONCE(d); + \ + WRITE_ONCE(c, 3); } / \ { x =3D READ_ONCE(a); + WRITE_ONCE(d, 4); }---- --->{ y =3D READ_ONCE(b); + + +EJEMPLOS DE SECUENCIAS DE BARRERA DE MEMORIA +-------------------------------------------- + +En primer lugar, las barreras de escritura act=C3=BAan como orden parcial = en las +operaciones de store. Considere la siguiente secuencia de eventos: + + CPU 1 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + STORE A =3D 1 + STORE B =3D 2 + STORE C =3D 3 + + STORE D =3D 4 + STORE E =3D 5 + +Esta secuencia de eventos es finalizado para con el sistema de coherencia +de memoria en un orden que el resto del sistema podr=C3=ADa percibir como = el +conjunto desordenado { STORE A, STORE B, STORE C} todo ocurriendo antes del +conjunto desordenado { STORE D, STORE E}: + + + +-------+ : : + | | +------+ + | |------>| C=3D3 | } /\ + | | : +------+ }----- \ -----> Eventos perceptibles para + | | : | A=3D1 | } \/ el resto del sistema + | | : +------+ } + | CPU 1 | : | B=3D2 | } + | | +------+ } + | | wwwwwwwwwwwwwwww } <--- En este momento la barrera de + | | +------+ } escritura requiere que todos los + | | : | E=3D5 | } stores anteriores a la barrera + | | : +------+ } sean confirmados antes de que otros + | |------>| D=3D4 | } store puedan suceder + | | +------+ + +-------+ : : + | + | Secuencia por la cual los stores son confirmados al + | sistema de memoria por parte del CPU 1 + V + +En segundo lugar, las barreras de dependencia de direcci=C3=B3n act=C3=BAa= n como +=C3=B3rdenes parciales sobre la direcci=C3=B3n de cargas dependientes. Con= sidere la +siguiente secuencia de eventos: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + { B =3D 7; X =3D 9; Y =3D 8; C =3D &Y } + STORE A =3D 1 + STORE B =3D 2 + + STORE C =3D &B LOAD X + STORE D =3D 4 LOAD C (consigue &B) + LOAD *C (lee B) + +Sin intervenci=C3=B3n, la CPU 2 puede percibir los eventos en la CPU 1 en = orden +aleatorio a efectos pr=C3=A1cticos, a pesar de la barrera de escritura emi= tida +por la CPU 1: + + +-------+ : : : : + | | +------+ +-------+ | Secuencia de + | |------>| B=3D2 |----- --->| Y->8 | | actualizado de + | | : +------+ \ +-------+ | percepci=C3=B3n en C= PU 2 + | CPU 1 | : | A=3D1 | \ --->| C->&Y | V + | | +------+ | +-------+ + | | wwwwwwwwwwwwwwww | : : + | | +------+ | : : + | | : | C=3D&B |--- | : : +-------+ + | | : +------+ \ | +-------+ | | + | |------>| D=3D4 | ----------->| C->&B |------>| | + | | +------+ | +-------+ | | + +-------+ : : | : : | | + | : : | | + | : : | CPU 2 | + | +-------+ | | + Percepci=C3=B3n de B ---> | | B->7 |------>| | + aparentemente incorrecta! | +-------+ | | + | : : | | + | +-------+ | | + La carga de X frena ---> \ | X->9 |------>| | + el mantenimiento de \ +-------+ | | + la coherencia de B ----->| B->2 | +-------+ + +-------+ + : : + + +En el ejemplo anterior, la CPU 2 percibe que B es 7, a pesar de la carga de +*C (que ser=C3=ADa B) viniendo despu=C3=A9s del LOAD de C. + +Sin embargo, si se colocara una barrera de dependencia de direcci=C3=B3n e= ntre +la carga de C y la carga de *C (es decir: B) en la CPU 2: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + { B =3D 7; X =3D 9; Y =3D 8; C =3D &Y } + STORE A =3D 1 + STORE B =3D 2 + + STORE C =3D &B LOAD X + STORE D =3D 4 LOAD C (consigue &B) + + LOAD *C (reads B) + +entonces ocurrir=C3=A1 lo siguiente: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| B=3D2 |----- --->| Y->8 | + | | : +------+ \ +-------+ + | CPU 1 | : | A=3D1 | \ --->| C->&Y | + | | +------+ | +-------+ + | | wwwwwwwwwwwwwwww | : : + | | +------+ | : : + | | : | C=3D&B |--- | : : +-------+ + | | : +------+ \ | +-------+ | | + | |------>| D=3D4 | ----------->| C->&B |------>| | + | | +------+ | +-------+ | | + +-------+ : : | : : | | + | : : | | + | : : | CPU 2 | + | +-------+ | | + | | X->9 |------>| | + | +-------+ | | + Se asegura de que ---> \ aaaaaaaaaaaaaaaaa | | + los efectos anteriores al \ +-------+ | | + store de C sean percibidos ----->| B->2 |------>| | + por los siguientes loads +-------+ | | + : : +-------+ + + +Y en tercer lugar, una barrera de lectura act=C3=BAa como un orden parcial= sobre +las cargas. Considere la siguiente secuencia de eventos: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + { A =3D 0, B =3D 9 } + STORE A=3D1 + + STORE B=3D2 + LOAD B + LOAD A + +Sin intervenci=C3=B3n, la CPU 2 puede elegir percibir los eventos en la CP= U 1 en +alg=C3=BAn orden aleatorio a efectos pr=C3=A1cticos, a pesar de la barrera= de +escritura emitida por la CPU 1: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| A=3D1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=3D2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| B->2 |------>| | + | +-------+ | CPU 2 | + | | A->0 |------>| | + | +-------+ | | + | : : +-------+ + \ : : + \ +-------+ + ---->| A->1 | + +-------+ + : : + +Sin embargo, si se colocara una barrera de lectura entre la carga de B y la +carga de A en la CPU 2: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + { A =3D 0, B =3D 9 } + STORE A=3D1 + + STORE B=3D2 + LOAD B + + LOAD A + +entonces el orden parcial impuesto por la CPU 1 ser=C3=A1 percibido +correctamente por la CPU 2: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| A=3D1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=3D2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| B->2 |------>| | + | +-------+ | CPU 2 | + | : : | | + | : : | | + En este punto la barrera ----> \ rrrrrrrrrrrrrrrrr | | + de lectura consigue que \ +-------+ | | + todos los efectos anteriores ---->| A->1 |------>| | + al almacenamiento de B sean +-------+ | | + perceptibles por la CPU 2 : : +-------+ + + +Para ilustrar esto de manera m=C3=A1s completa, considere lo que podr=C3= =ADa pasar si +el c=C3=B3digo conten=C3=ADa una carga de A a cada lado de la barrera de l= ectura: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + { A =3D 0, B =3D 9 } + STORE A=3D1 + + STORE B=3D2 + LOAD B + LOAD A [primer load de A] + + LOAD A [segundo load de A] + +Aunque las dos cargas de A ocurren despu=C3=A9s de la carga de B, ambas pu= eden +obtener diferentes valores: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| A=3D1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=3D2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| B->2 |------>| | + | +-------+ | CPU 2 | + | : : | | + | : : | | + | +-------+ | | + | | A->0 |------>| 1st | + | +-------+ | | + En este punto la barrera ----> \ rrrrrrrrrrrrrrrrr | | + de lectura consigue que \ +-------+ | | + todos los efectos anteriores ---->| A->1 |------>| | + al almacenamiento de B sean +-------+ | | + perceptibles por la CPU 2 : : +-------+ + +Pero puede ser que la actualizaci=C3=B3n a A desde la CPU 1 se vuelva +perceptible para la CPU 2 antes de que la barrera de lectura se complete de +todos modos: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| A=3D1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=3D2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| B->2 |------>| | + | +-------+ | CPU 2 | + | : : | | + \ : : | | + \ +-------+ | | + ---->| A->1 |------>| 1st | + +-------+ | | + rrrrrrrrrrrrrrrrr | | + +-------+ | | + | A->1 |------>| 2nd | + +-------+ | | + : : +-------+ + +La garant=C3=ADa es que la segunda carga siempre dar=C3=A1 como resultado = A =3D=3D 1 si +la carga de B result=C3=B3 en B =3D=3D 2. No existe tal garant=C3=ADa para= la primera +carga de A; esto puede dar como resultado A =3D=3D 0 o A =3D=3D 1. + + +BARRERAS DE MEMORIA DE LECTURA FRENTE A ESPECULACI=C3=93N DE CARGA +------------------------------------------------------------- + +Muchas CPU especulan con las cargas: es decir, ven que necesitar=C3=A1n ca= rgar +un elemento de la memoria, y encuentran un momento en el que no est=C3=A1n +usando el bus para ning=C3=BAn otra carga, y tambi=C3=A9n en la carga por = adelantado, +aunque en realidad no lo hayan llegado a ese punto en el flujo de ejecuci= =C3=B3n +de instrucciones todav=C3=ADa. Esto permite que la instrucci=C3=B3n de car= ga real +potencialmente complete de inmediato, porque la CPU ya tiene el valor a +mano. + +Puede resultar que la CPU en realidad no necesitara el valor, tal vez +porque una condici=C3=B3n eludi=C3=B3 la carga, en cuyo caso puede descart= ar el valor +o simplemente almacenar en cach=C3=A9 para su uso posterior. + +Considere: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + LOAD B + DIVIDE } Instrucciones de divisi=C3=B3n + DIVIDE } tardan mucho en terminar + LOAD A + +donde DIVIDE es DIVIDIR. Que podr=C3=ADa aparecer como esto: + + : : +-------+ + +-------+ | | + --->| B->2 |------>| | + +-------+ | CPU 2 | + : :DIVIDE | | + +-------+ | | + La CPU ocupada con la divisi=C3=B3n ---> --->| A->0 |~~~~ | | + especula sobre el LOAD de A +-------+ ~ | | + : : ~ | | + : :DIVIDE | | + : : ~ | | + Una vez completadas las divisiones --> : : ~-->| | + la CPU puede realizar el : : | | + LOAD con efecto inmediato : : +-------+ + + +Colocando una barrera de lectura o una barrera de dependencia de direcci= =C3=B3n +justo antes de la segundo carga: + + + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + LOAD B + DIVIDE + DIVIDE + + LOAD A + +obligar=C3=A1 a reconsiderar cualquier valor obtenido especulativamente en= una +medida dependiente del tipo de barrera utilizada. Si no se hizo ning=C3=BAn +cambio en la ubicaci=C3=B3n de memoria especulada, entonces el valor espec= ulado +solo se usar=C3=A1: + + : : +-------+ + +-------+ | | + --->| B->2 |------>| | + +-------+ | CPU 2 | + : :DIVIDE | | + +-------+ | | + La CPU ocupada con la divisi=C3=B3n ---> --->| A->0 |~~~~ | | + especula sobre el LOAD de A +-------+ ~ | | + : : ~ | | + : :DIVIDE | | + : : ~ | | + : : ~ | | + rrrrrrrrrrrrrrrr~ | | + : : ~ | | + : : ~-->| | + : : | | + : : +-------+ + + +pero si hab=C3=ADa una actualizaci=C3=B3n o una invalidaci=C3=B3n de otra = CPU pendiente, +entonces la especulaci=C3=B3n ser=C3=A1 cancelada y el valor recargado: + + : : +-------+ + +-------+ | | + --->| B->2 |------>| | + +-------+ | CPU 2 | + : :DIVIDE | | + +-------+ | | + La CPU ocupada con la divisi=C3=B3n ---> --->| A->0 |~~~~ | | + especula sobre el LOAD de A +-------+ ~ | | + : : ~ | | + : :DIVIDE | | + : : ~ | | + : : ~ | | + rrrrrrrrrrrrrrrrr | | + +-------+ | | + La especulaci=C3=B3n es descartada ---> --->| A->1 |------>| | + y un valor actualizado +-------+ | | + es conseguido : : +-------+ + +ATOMICIDAD MULTICOPIA +--------------------- + +La atomicidad multicopia es una noci=C3=B3n profundamente intuitiva sobre = el +orden que no es siempre proporcionada por los sistemas inform=C3=A1ticos r= eales, +a saber, que un determinada store se vuelve visible al mismo tiempo para +todos las CPU o, alternativamente, que todas las CPU acuerdan el orden en +que todos los stores se vuelven visibles. Sin embargo, el soporte para +atomicidad multicopia completa descartar=C3=ADa valiosas optimizaciones +hardware, por lo que una versi=C3=B3n m=C3=A1s d=C3=A9bil conocida como ``= otra atomicidad +multicopia'' en cambio, solo garantiza que un store dado se vuelva visible +al mismo tiempo en todas las -otras- CPUs. El resto de este documento +discute esta versi=C3=B3n m=C3=A1s d=C3=A9bil, pero por brevedad lo llamar= emos simplemente +``atomicidad multicopia''. + +El siguiente ejemplo demuestra la atomicidad multicopia: + + CPU 1 CPU 2 CPU 3 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + { X =3D 0, Y =3D 0 } + STORE X=3D1 r1=3DLOAD X (reads 1) LOAD Y (reads 1) + + STORE Y=3Dr1 LOAD X + +Suponga que la carga de la CPU 2 desde X devuelve 1, que luego almacena en +Y, y la carga de la CPU 3 desde Y devuelve 1. Esto indica que el store de +la CPU 1 a X precede a la carga de la CPU 2 desde X y el store de esa CPU 2 +a Y precede la carga de la CPU 3 desde Y. Adem=C3=A1s, las barreras de mem= oria +garantizan que la CPU 2 ejecuta su carga antes que su almacenamiento, y la +CPU 3 carga desde Y antes de cargar desde X. La pregunta entonces es +"=C2=BFPuede la carga de la CPU 3 desde X devolver 0?" + +Debido a que la carga de la CPU 3 desde X en cierto sentido viene despu=C3= =A9s +de la carga de la CPU 2, es natural esperar que la carga de la CPU 3 desde +X deba devolver 1. Esta expectativa se deriva de la atomicidad multicopia: +si una carga que se ejecuta en la CPU B sigue una carga de la misma +variable que se ejecuta en la CPU A (y la CPU A no almacen=C3=B3 originalm= ente +el valor que ley=C3=B3), entonces en sistemas at=C3=B3micos multicopia, la= carga de +la CPU B debe devolver el mismo valor que hizo la carga de la CPU A o alg= =C3=BAn +valor posterior. Sin embargo, el kernel Linux no requiere que los sistemas +sean at=C3=B3micos multicopia. + +El uso de una barrera de memoria general en el ejemplo anterior compensa +cualquier falta de atomicidad multicopia. En el ejemplo, si la carga de la +CPU 2 de X devuelve 1 y la carga de la CPU 3 de Y devuelve 1, entonces la +carga de la CPU 3 desde X debe de hecho tambi=C3=A9n devolver 1. + +Sin embargo, las dependencias, las barreras de lectura y las barreras de +escritura no siempre son capaces de compensar la atomicidad no multicopia. +Por ejemplo, supongamos que la barrera general de la CPU 2 se elimina del +ejemplo anterior, dejando solo la dependencia de datos que se muestra a +continuaci=C3=B3n: + + CPU 1 CPU 2 CPU 3 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + { X =3D 0, Y =3D 0 } + STORE X=3D1 r1=3DLOAD X (escribe 1) LOAD Y (lee 1) + + STORE Y=3Dr1 LOAD X (lee 0) + +Esta sustituci=C3=B3n permite que la atomicidad no multicopia se desenfren= e: en +este ejemplo, es perfectamente legal que la carga de la CPU 2 desde X +devuelva 1, la carga de la CPU 3 desde Y devuelva 1, y su carga desde X +tenga valor 0. + +El punto clave es que aunque la dependencia de datos de la CPU 2 ordena su +carga y store, no garantiza ordenar el store de la CPU 1. De forma que, si +este ejemplo se ejecuta en un sistema at=C3=B3mico no multicopia donde las= CPU 1 +y 2 comparten un buffer de almacenamiento o un nivel de cach=C3=A9, la CPU= 2 +podr=C3=ADa tener acceso anticipado de escritura a CPU 1. Por lo tanto, se +requieren barreras generales para garantizar que todas las CPU acurden el +orden combinado de accesos m=C3=BAltiples. + +Las barreras generales pueden compensar no solo la atomicidad no +multicopia, pero tambi=C3=A9n pueden generar orden adicional que puede ase= gurar +que -todas- las CPU percibir=C3=A1n el mismo orden de -todas- las operacio= nes. +Por el contrario, una cadena de parejas de liberaci=C3=B3n-adquisici=C3=B3= n no +proporciona este orden adicional, lo que significa que solo se garantiza +que las CPU de la cadena est=C3=A9n de acuerdo en el orden combinado de los +accesos. Por ejemplo, cambiando a c=C3=B3digo C en deferencia al fantasma = de +Herman Hollerith: + + int u, v, x, y, z; + + void cpu0(void) + { + r0 =3D smp_load_acquire(&x); + WRITE_ONCE(u, 1); + smp_store_release(&y, 1); + } + + void cpu1(void) + { + r1 =3D smp_load_acquire(&y); + r4 =3D READ_ONCE(v); + r5 =3D READ_ONCE(u); + smp_store_release(&z, 1); + } + + void cpu2(void) + { + r2 =3D smp_load_acquire(&z); + smp_store_release(&x, 1); + } + + void cpu3(void) + { + WRITE_ONCE(v, 1); + smp_mb(); + r3 =3D READ_ONCE(u); + } + +Dado que cpu0(), cpu1() y cpu2() participan en una cadena de parejas +smp_store_release()/smp_load_acquire(), el siguiente resultado estar=C3=ADa +prohibido: + + r0 =3D=3D 1 && r1 =3D=3D 1 && r2 =3D=3D 1 + +Adem=C3=A1s, debido a la relaci=C3=B3n liberaci=C3=B3n-adquisici=C3=B3n en= tre cpu0() y cpu1(), +cpu1() debe ver las escrituras de cpu0(), de modo que el siguiente +resultado estar=C3=ADa prohibido: + + r1 =3D=3D 1 && r5 =3D=3D 0 + +Sin embargo, el orden proporcionado por una cadena de +liberaci=C3=B3n-adquisici=C3=B3n es local a las CPU que participan en esa = cadena y no +se aplica a cpu3(), al menos aparte de los stores. Por lo tanto, es posible +el siguiente resultado: + + r0 =3D=3D 0 && r1 =3D=3D 1 && r2 =3D=3D 1 && r3 =3D=3D 0 && r4 =3D=3D 0 + +Por otro lado, tambi=C3=A9n el siguiente resultado es posible: + + r0 =3D=3D 0 && r1 =3D=3D 1 && r2 =3D=3D 1 && r3 =3D=3D 0 && r4 =3D=3D 0 &= & r5 =3D=3D 1 + +Aunque cpu0(), cpu1() y cpu2() ver=C3=A1n sus respectivas lecturas y escri= turas +en orden, las CPU que no participan en la cadena de liberaci=C3=B3n-adquis= ici=C3=B3n +pueden estar en desacuerdo con el orden. Este desacuerdo se debe al hecho +de que las instrucciones de barrera de memoria d=C3=A9biles utilizadas para +implementar smp_load_acquire() y smp_store_release() no son necesarios para +ordenar stores anteriores contra cargas posteriores en todos los casos. +Esto significa que cpu3() puede ver el store de cpu0() suceder -despu=C3= =A9s- de +la carga de cpu1() desde v, aunque tanto cpu0() como cpu1() est=C3=A1n de +acuerdo en que estas dos operaciones ocurrieron en el orden previsto. + +Sin embargo, tenga en cuenta que smp_load_acquire() no es m=C3=A1gico. En +particular, simplemente lee de su argumento en orden. Es decir, -no- +asegura que se leer=C3=A1 cualquier valor en particular. Por lo tanto, los +siguiente resultados son posibles: + + r0 =3D=3D 0 && r1 =3D=3D 0 && r2 =3D=3D 0 && r5 =3D=3D 0 + +Tenga en cuenta que este resultado puede ocurrir incluso en un m=C3=ADtico +sistema, consistente en secuencia, donde nunca se reordena nada. + +Para reiterar, si su c=C3=B3digo requiere un orden completo de todas las +operaciones, utilice barreras generales en todo momento. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D +BARRERAS EXPL=C3=8DCITAS DEL KERNEL +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + +El kernel Linux tiene una variedad de diferentes barreras que act=C3=BAan a +diferentes niveles: + + (*) Barrera del compilador. + + (*) Barreras de memoria de la CPU. + + +BARRERA DEL COMPILADOR +----------------------- + +El kernel de Linux tiene una funci=C3=B3n de barrera del compilador expl= =C3=ADcita +que evita que el el compilador mueva los accesos a la memoria de cualquier +lado al otro: + + barrier(); + +Esta es una barrera general: no hay variantes de barrier() para casos de +lectura-lectura o escritura-escritura. Sin embargo, READ_ONCE() y +WRITE_ONCE() pueden ser considerado como formas d=C3=A9biles de barrier() = que +afectan solo espec=C3=ADficos accesos marcados por READ_ONCE() o WRITE_ONC= E(). + +La funci=C3=B3n barrier() produce los siguientes efectos: + + (*) Evita que el compilador reordene los accesos tras barrier() para + preceder a cualquier acceso que preceda a barrier(). Un ejemplo de uso + de esta propiedad es facilitar la comunicaci=C3=B3n entre c=C3=B3digo= del + interrupt-handler (encargo de gestionar interrupciones) y el c=C3=B3d= igo + que fue interrumpido. + + (*) Dentro de un bucle ("loop"), obliga al compilador a cargar las + variables utilizadas en ese loop condicional en cada paso a trav=C3= =A9s de + ese loop. + +Las funciones READ_ONCE() y WRITE_ONCE() pueden evitar cualquier cantidad +de optimizaciones que, si bien son perfectamente seguras en c=C3=B3digo de= un +solo subproceso, pueden resultar fatales en c=C3=B3digo concurrente. Aqu= =C3=AD hay +algunos ejemplos de tal tipo de optimizaciones: + +(*) El compilador est=C3=A1 en su derecho de reordenar cargas y stores de = la + misma variable, y en algunos casos, la CPU est=C3=A1 dentro de su + derecho de reordenar cargas a la misma variable. Esto significa que + el siguiente c=C3=B3digo: + + a[0] =3D x; + a[1] =3D x; + + Podr=C3=ADa resultar en un valor m=C3=A1s antiguo de x almacenado en = a[1] que en + a[0]. Evite que tanto el compilador como la CPU hagan esto de la + siguiente manera: + + a[0] =3D READ_ONCE(x); + a[1] =3D READ_ONCE(x); + + En resumen, READ_ONCE() y WRITE_ONCE() proporcionan coherencia de + cach=C3=A9 para accesos desde m=C3=BAltiples CPUs a una sola variable. + + (*) El compilador tiene derecho a juntar cargas sucesivas de la misma + variable. Tal fusi=C3=B3n puede hacer que el compilador "optimice= " el + siguiente c=C3=B3digo: + + while (tmp =3D a) + hacer_algo_con(tmp); + + en el siguiente c=C3=B3digo, que, aunque en cierto sentido es leg= =C3=ADtimo + para un c=C3=B3digo de un solo subproceso, es casi seguro que no e= s lo + que el desarrollador pretend=C3=ADa: + + if (tmp =3D a) + for (;;) + hacer_algo_con(tmp); + + Use READ_ONCE() para evitar que el compilador le haga esto: + + while (tmp =3D READ_ONCE(a)) + hacer_algo_con(tmp); + + (*) El compilador tiene derecho a recargar una variable, por ejemplo, + en los casos en que la alta presi=C3=B3n de los registros impida que el + compilador mantenga todos los datos de inter=C3=A9s en registros. El + compilador podr=C3=ADa por lo tanto, optimizar la variable 'tmp' de nu= estro + ejemplo anterior: + + while (tmp =3D a) + hacer_algo_con(tmp); + + Esto podr=C3=ADa resultar en el siguiente c=C3=B3digo, que es perfect= amente + seguro en c=C3=B3digo de subproceso =C3=BAnico, pero puede ser fatal = en c=C3=B3digo + concurrente: + + while (a) + hacer_algo_con(a); + + Por ejemplo, la versi=C3=B3n optimizada de este c=C3=B3digo podr=C3=AD= a resultar en + pasar un cero a hacer_algo_con() en el caso de que la variable a sea + modificada por alguna otra CPU, entre la instrucci=C3=B3n "while" y la + llamada a hacer_algo_con(). + + De nuevo, use READ_ONCE() para evitar que el compilador haga esto: + + while (tmp =3D READ_ONCE(a)) + hacer_algo_con(tmp); + + Tenga en cuenta que si el compilador se queda sin registros, podr=C3= =ADa + guardar tmp en la pila ("stack"). El overhead (coste en eficiencia) de + este guardado y posterior restauraci=C3=B3n es por lo que los compila= dores + recargan las variables. Hacerlo es perfectamente seguro para c=C3=B3d= igo de + subproceso =C3=BAnico, por lo que debe informar al compilador sobre l= os + casos donde no sea seguro. + + (*) El compilador est=C3=A1 en su derecho de omitir una carga por complet= o si + sabe cual ser=C3=A1 su valor. Por ejemplo, si el compilador puede pro= bar + que el valor de la variable 'a' siempre es cero, puede optimizar este + c=C3=B3digo: + + while (tmp =3D a) + hacer_algo_con(tmp); + + En esto: + + do { } while (0); + + Esta transformaci=C3=B3n es una victoria para un c=C3=B3digo de un so= lo + subproceso, porque se deshace de una carga y un branch. El problema es + que el compilador llevar=C3=A1 a cabo su prueba asumiendo que la CPU = actual + es la =C3=BAnica actualizando la variable 'a'. Si la variable 'a' es + compartida, entonces la prueba del compilador ser=C3=A1 err=C3=B3nea.= Use + READ_ONCE() para decirle al compilador que no sabe tanto como cree: + + while (tmp =3D READ_ONCE(a)) + hacer_algo_con(tmp); + + Pero, por favor, tenga en cuenta que el compilador tambi=C3=A9n est= =C3=A1 + observando de cerca lo que usted hace con el valor despu=C3=A9s de + READ_ONCE(). Por ejemplo, suponga que Ud. hace lo siguiente y MAX es + una macro de preprocesador con el valor 1: + + while ((tmp =3D READ_ONCE(a)) % MAX) + hacer_algo_con(tmp); + + Entonces el compilador sabe que el resultado del operador "%" aplicado + a MAX siempre ser=C3=A1 cero, nuevamente permitiendo que el compilador + optimice el c=C3=B3digo hasta su casi inexistencia. (A=C3=BAn se carg= ar=C3=A1 desde + la variable 'a'.) + + (*) De manera similar, el compilador tiene derecho a omitir un store por + completo si sabe que la variable ya tiene el valor almacenado. + Nuevamente, el compilador asume que la CPU actual es la =C3=BAnica que + almacena la variable, lo que puede hacer que el compilador haga + algo incorrecto para las variables compartidas. Por ejemplo, suponga + que tiene lo siguiente: + + a =3D 0; + ... C=C3=B3digo que no almacena la variable a ... + a =3D 0; + + El compilador observa que el valor de la variable 'a' ya es cero, por + lo que bien podr=C3=ADa omitir el segundo store. Esto supondr=C3=ADa = una fatal + sorpresa, si alguna otra CPU hubiera almacenado la variable 'a' + mientras tanto. + + Use WRITE_ONCE() para evitar que el compilador haga este tipo de + suposici=C3=B3n equivocada: + + WRITE_ONCE(a, 0); + ... C=C3=B3digo que no almacena la variable a ... + WRITE_ONCE(a, 0); + + (*) El compilador tiene derecho a reordenar los accesos a memoria a menos + que le diga que no. Por ejemplo, considere la siguiente interacci=C3= =B3n + entre el c=C3=B3digo de nivel de proceso y un controlador de interrup= ci=C3=B3n: + + void nivel_de_procesamiento(void) + { + msg =3D ACQUIRE_mensaje(); + flag =3D true; + } + + void controlador_interrupcion(void) + { + if (flag) + procesar_mensaje(msg); + } + + No hay nada que impida que el compilador transforme + nivel_de_procesamiento() a lo siguiente, que de hecho, bien podr=C3= =ADa ser + una victoria para c=C3=B3digo de un solo subproceso: + + void nivel_de_procesamiento(void) + { + flag =3D true; + msg =3D ACQUIRE_mensaje(); + } + + Si la interrupci=C3=B3n ocurre entre estas dos declaraciones, entonces + controlador_interrupcion() podr=C3=ADa recibir un mensaje ilegible. U= se + READ_ONCE() para evitar esto de la siguiente manera: + + void nivel_de_procesamiento(void) + { + WRITE_ONCE(msg, ACQUIRE_mensaje()); + WRITE_ONCE(flag, true); + } + + void controlador_interrupcion(void) + { + if (READ_ONCE(flag)) + procesar_mensaje(READ_ONCE(msg)); + } + + Tenga en cuenta que los envoltorios ("wrappers") READ_ONCE() y + WRITE_ONCE() en controlador_interrupcion() son necesarios si este + controlador de interrupciones puede ser interrumpido por algo que + tambi=C3=A9n accede a 'flag' y 'msg', por ejemplo, una interrupci=C3= =B3n anidada + o un NMI. De lo contrario, READ_ONCE() y WRITE_ONCE() no son + necesarios en controlador_interrupcion() aparte de con fines de + documentaci=C3=B3n. (Tenga tambi=C3=A9n en cuenta que las interrupcio= nes + anidadas no ocurren t=C3=ADpicamente en los kernels Linux modernos, de + hecho, si un controlador de interrupciones regresa con interrupciones + habilitadas, obtendr=C3=A1 un WARN_ONCE().) + + Debe suponer que el compilador puede mover READ_ONCE() y WRITE_ONCE() + a c=C3=B3digo que no contiene READ_ONCE(), WRITE_ONCE(), barrier(), o + primitivas similares. + + Este efecto tambi=C3=A9n podr=C3=ADa lograrse usando barrier(), pero = READ_ONCE() + y WRITE_ONCE() son m=C3=A1s selectivos: Con READ_ONCE() y WRITE_ONCE(= ), el + compilador solo necesita olvidar el contenido de ubicaciones de + memoria indicadas, mientras que con barrier() el compilador debe + descartar el valor de todas las ubicaciones de memoria que tiene + actualmente almacenadas en cach=C3=A9, en cualquier registro de la m= =C3=A1quina. + Por supuesto, el compilador tambi=C3=A9n debe respetar el orden en que + ocurren READ_ONCE() y WRITE_ONCE(), aunque la CPU, efectivamente, no + necesita hacerlo. + + (*) El compilador tiene derecho a inventar stores para una variable, + como en el siguiente ejemplo: + + if (a) + b =3D a; + else + b =3D 42; + + El compilador podr=C3=ADa ahorrar un branch al optimizar esto de la + siguiente manera: + + b =3D 42; + if (a) + b =3D a; + + En el c=C3=B3digo de un solo subproceso, esto no solo es seguro, sino= que + tambi=C3=A9n ahorra un branch. Desafortunadamente, en c=C3=B3digo con= currente, + esta optimizaci=C3=B3n podr=C3=ADa causar que alguna otra CPU vea un = valor falso + de 42, incluso si la variable 'a' nunca fue cero, al cargar la + variable 'b'. Use WRITE_ONCE() para evitar esto de la siguiente + manera: + + if (a) + WRITE_ONCE(b, a); + else + WRITE_ONCE(b, 42); + + El compilador tambi=C3=A9n puede inventar cargas. Estos casos suelen s= er + menos perjudiciales, pero pueden dar como resultado "bouncing" de la + l=C3=ADnea de cach=C3=A9 y, por lo tanto, bajo rendimiento y escalabil= idad. + Utilice READ_ONCE() para evitar cargas inventadas. + + (*) Para ubicaciones de memoria alineadas cuyo tama=C3=B1o les permita + acceder con una sola instrucci=C3=B3n de referencia de memoria, evite= el + "desgarro de la carga" (load tearing) y "desgarro del store" (store + tearing), en el que un solo gran acceso es reemplazado por m=C3=BAlti= ples + accesos menores. Por ejemplo, dada una arquitectura que tiene + instrucciones de almacenamiento de 16 bits con campos inmediatos de 7 + bits, el compilador podr=C3=ADa tener la tentaci=C3=B3n de usar dos + instrucciones inmediatas de almacenamiento de 16 bits para implementar + el siguiente store de 32 bits: + + p =3D 0x00010002; + + Tenga en cuenta que GCC realmente usa este tipo de optimizaci=C3=B3n,= lo + cual no es sorprendente dado que probablemente costar=C3=ADa m=C3=A1s= de dos + instrucciones el construir la constante y luego almacenarla. Por lo + tanto, esta optimizaci=C3=B3n puede ser una victoria en un c=C3=B3dig= o de un + solo subproceso. De hecho, un error reciente (desde que se solucion= =C3=B3) + hizo que GCC usara incorrectamente esta optimizaci=C3=B3n en un store + vol=C3=A1til. En ausencia de tales errores, el uso de WRITE_ONCE() ev= ita el + desgarro del store en el siguiente ejemplo: + + struct __attribute__((__packed__)) foo { + short a; + int b; + short c; + }; + struct foo foo1, foo2; + ... + + foo2.a =3D foo1.a; + foo2.b =3D foo1.b; + foo2.c =3D foo1.c; + + Debido a que no hay envoltorios READ_ONCE() o WRITE_ONCE() y no + hay markings vol=C3=A1tiles, el compilador estar=C3=ADa en su derecho= de + implementar estas tres declaraciones de asignaci=C3=B3n como un par de + cargas de 32 bits, seguido de un par de stores de 32 bits. Esto + resultar=C3=ADa en una carga con desgarro en 'foo1.b' y store del des= garro + en 'foo2.b'. READ_ONCE() y WRITE_ONCE() nuevamente evitan el desgarro + en este ejemplo: + + foo2.a =3D foo1.a; + WRITE_ONCE(foo2.b, READ_ONCE(foo1.b)); + foo2.c =3D foo1.c; + +Aparte de esto, nunca es necesario usar READ_ONCE() y WRITE_ONCE() en una +variable que se ha marcado como vol=C3=A1til. Por ejemplo, dado que 'jiffi= es' +est=C3=A1 marcado como vol=C3=A1til, nunca es necesario usar READ_ONCE(jif= fies). La +raz=C3=B3n de esto es que READ_ONCE() y WRITE_ONCE() se implementan como +conversiones vol=C3=A1tiles, lo que no tiene efecto cuando su argumento ya= est=C3=A1 +marcado como vol=C3=A1til. + +Tenga en cuenta que estas barreras del compilador no tienen un efecto +directo en la CPU, que luego puede reordenar las cosas como quiera. + + +BARRERAS DE MEMORIA DE LA CPU +----------------------------- + +El kernel de Linux tiene siete barreras b=C3=A1sicas de memoria de CPU: + +TIPO OBLIGATORIO SMP CONDICIONAL +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D +GENERAL mb() smp_mb() +WRITE wmb() smp_wmb() +READ rmb() smp_rmb() +DEPEDENCIA DE DIRECCI=C3=93N READ_ONCE() + + +Todas las barreras de memoria, excepto las barreras de dependencia de +direcciones, implican una barrera del compilador. Las dependencias de +direcciones no imponen ning=C3=BAn orden de compilaci=C3=B3n adicional. + +Adem=C3=A1s: en el caso de las dependencias de direcciones, se esperar=C3= =ADa que el +compilador emita las cargas en el orden correcto (por ejemplo, `a[b]` +tendr=C3=ADa que cargar el valor de b antes de cargar a[b]), sin embargo, = no hay +garant=C3=ADa alguna en la especificaci=C3=B3n de C sobre que el compilado= r no puede +especular el valor de b (por ejemplo, es igual a 1) y carga a[b] antes que +b (ej. tmp =3D a[1]; if (b !=3D 1) tmp =3D a[b]; ). Tambi=C3=A9n existe el= problema de +que un compilador vuelva a cargar b despu=C3=A9s de haber cargado a[b], te= niendo +as=C3=AD una copia m=C3=A1s nueva de b que a[b]. A=C3=BAn no se ha consegu= ido un consenso +acerca de estos problemas, sin embargo, el macro READ_ONCE() es un buen +lugar para empezar a buscar. + +Las barreras de memoria SMP se reducen a barreras de compilador cuando se +compila a monoprocesador, porque se supone que una CPU parecer=C3=A1 ser +auto-consistente, y ordenar=C3=A1 correctamente los accesos superpuestos +respecto a s=C3=AD misma. Sin embargo, consulte la subsecci=C3=B3n "Guests= de +m=C3=A1quinas virtuales" mas adelante. + +[!] Tenga en cuenta que las barreras de memoria SMP _deben_ usarse para +controlar el orden de referencias a memoria compartida en sistemas SMP, +aunque el uso de bloqueo en su lugar sea suficiente. + +Las barreras obligatorias no deben usarse para controlar los efectos de +SMP, ya que dichas barreras imponen una sobrecarga innecesaria en los +sistemas SMP y UP. Se pueden, sin embargo, usar para controlar los efectos +MMIO en los accesos a trav=C3=A9s de ventanas E/S de memoria relajada. Est= as +barreras son necesarias incluso en sistemas que no son SMP, ya que afectan +al orden en que las operaciones de memoria aparecen en un dispositivo, al +prohibir tanto al compilador como a la CPU que sean reordenados. + + +Hay algunas funciones de barrera m=C3=A1s avanzadas: + + (*) smp_store_mb(var, valor) + + Asigna el valor a la variable y luego inserta una barrera de memoria + completa despu=C3=A9s de ella. No se garantiza insertar nada m=C3=A1s= que una + barrera del compilador en una compilaci=C3=B3n UP. + + + (*) smp_mb__before_atomic(); + (*) smp_mb__after_atomic(); + + Estos se pueden usar con funciones RMW at=C3=B3micas que no implican + barreras de memoria, pero donde el c=C3=B3digo necesita una barrera de + memoria. Ejemplos de funciones RMW at=C3=B3micas que no implican una + barrera de memoria son, por ejemplo, agregar, restar, operaciones + condicionales (fallidas), funciones _relaxed, pero no atomic_read o + atomic_set. Un ejemplo com=C3=BAn donde se puede requerir una barrera= es + cuando se usan operaciones at=C3=B3micas como referencia de contador. + + Estos tambi=C3=A9n se utilizan para funciones at=C3=B3micas RMW bitop= que no + implican una barrera de memoria (como set_bit y clear_bit). + + Como ejemplo, considere una pieza de c=C3=B3digo que marca un objeto = como + muerto y luego disminuye el contador de referencias del objeto: + + obj->dead =3D 1; + smp_mb__before_atomic(); + atomic_dec(&obj->ref_count); + + Esto asegura que la marca de muerte en el objeto se perciba como + fijada *antes* de que disminuya el contador de referencia. + + Consulte Documentation/atomic_{t,bitops}.txt para obtener m=C3=A1s + informaci=C3=B3n. + + + (*) dma_wmb(); + (*) dma_rmb(); + (*) dma_mb(); + + Estos son usados con memoria consistente para garantizar el orden de + escrituras o lecturas de memoria compartida accesible tanto para la + CPU como para un dispositivo compatible con DMA. + + Por ejemplo, considere un controlador de dispositivo que comparte + memoria con otro dispositivo y usa un valor de estado del descriptor + para indicar si el descriptor pertenece al dispositivo o a la CPU, y + un "doorbell" (timbre, punto de acceso) para avisarle cuando haya + nuevos descriptores disponibles: + + if (desc->status !=3D DEVICE_OWN) { + /* no leer los datos hasta que tengamos el descriptor */ + dma_rmb(); + + /* leer/modificar datos */ + read_data =3D desc->data; + desc->data =3D write_data; + + /* flush de modificaciones antes de la actualizaci=C3=B3n de estado */ + dma_wmb(); + + /* asignar propiedad */ + desc->status =3D DEVICE_OWN; + + /* notificar al dispositivo de nuevos descriptores */ + writel(DESC_NOTIFY, doorbell); + } + + El dma_rmb() nos permite garantizar que el dispositivo ha liberado su + propiedad antes de que leamos los datos del descriptor, y el dma_wmb() + permite garantizar que los datos se escriben en el descriptor antes de + que el dispositivo pueda ver que ahora tiene la propiedad. El dma_mb() + implica tanto un dma_rmb() como un dma_wmb(). Tenga en cuenta que, al + usar writel(), no se necesita un wmb() anterior para garantizar que + las escrituras de la memoria cach=C3=A9 coherente se hayan completado= antes + escribiendo a la regi=C3=B3n MMIO. El writel_relaxed() m=C3=A1s barat= o no + proporciona esta garant=C3=ADa y no debe utilizarse aqu=C3=AD. + + Consulte la subsecci=C3=B3n "Efectos de barrera de E/S del kernel" pa= ra + obtener m=C3=A1s informaci=C3=B3n sobre accesorios de E/S relajados y= el archivo + Documentation/core-api/dma-api.rst para m=C3=A1s informaci=C3=B3n sob= re memoria + consistente. + + (*) pmem_wmb(); + + Es es para uso con memoria persistente para garantizar que los stores + para los que las modificaciones se escriben en el almacenamiento + persistente llegaron a dominio de durabilidad de la plataforma. + + Por ejemplo, despu=C3=A9s de una escritura no temporal en la regi=C3= =B3n pmem, + usamos pmem_wmb() para garantizar que los stores hayan alcanzado el + dominio de durabilidad de la plataforma. Esto garantiza que los stores + han actualizado el almacenamiento persistente antes de cualquier + acceso a datos o transferencia de datos causada por instrucciones + posteriores. Esto es adem=C3=A1s del orden realizado por wmb(). + + Para la carga desde memoria persistente, las barreras de memoria de + lectura existentes son suficientes para garantizar el orden de + lectura. + + (*) io_stop_wc(); + + Para accesos a memoria con atributos de combinaci=C3=B3n de escritura= (por + ejemplo, los devueltos por ioremap_wc(), la CPU puede esperar a que + los accesos anteriores se junten con posteriores. io_stop_wc() se + puede utilizar para evitar la combinaci=C3=B3n de accesos a memoria de + de escritura antes de esta macro, con los posteriores, cuando dicha + espera tenga implicaciones en el rendimiento. + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +BARRERAS DE MEMORIA IMPL=C3=8DCITAS DEL KERNEL +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Algunas de las otras funciones en el kernel Linux implican barreras de +memoria, entre estas encontramos funciones de bloqueo y planificaci=C3=B3n +("scheduling"). + +Esta especificaci=C3=B3n es una garant=C3=ADa _m=C3=ADnima_; cualquier arq= uitectura +particular puede proporcionar garant=C3=ADas m=C3=A1s sustanciales, pero n= o se puede +confiar en estas fuera de c=C3=B3digo espec=C3=ADfico de arquitectura. + + +FUNCIONES DE ADQUISICI=C3=93N DE CERROJO +----------------------------------- + +El kernel Linux tiene una serie de abstracciones de bloqueo: + + (*) spin locks (cerrojos en loop) + (*) R/W spin lock (cerrojos de escritura/lectura) + (*) mutex + (*) sem=C3=A1foros + (*) R/W sem=C3=A1foros + +En todos los casos existen variantes de las operaciones "ACQUIRE" y +"RELEASE" para cada uno de ellos. Todas estas operaciones implican ciertas +barreras: + + (1) Implicaciones de la operaci=C3=B3n ACQUIRE: + + Las operaciones de memoria emitidas despu=C3=A9s del ACQUIRE se compl= etar=C3=A1n + despu=C3=A9s de que la operaci=C3=B3n ACQUIRE haya finalizado. + + Las operaciones de memoria emitidas antes de ACQUIRE pueden + completarse despu=C3=A9s que la operaci=C3=B3n ACQUIRE se ha completa= do. + + (2) Implicaciones de la operaci=C3=B3n RELEASE: + + Las operaciones de memoria emitidas antes de la RELEASE se + completar=C3=A1n antes de que la operaci=C3=B3n de RELEASE se haya co= mpletado. + + Las operaciones de memoria emitidas despu=C3=A9s de la RELEASE pueden + completarse antes de que la operaci=C3=B3n de RELEASE se haya complet= ado. + + (3) Implicaci=C3=B3n de ACQUIRE vs ACQUIRE: + + Todas las operaciones ACQUIRE emitidas antes de otra operaci=C3=B3n + ACQUIRE ser=C3=A1n completadas antes de esa operaci=C3=B3n ACQUIRE. + + (4) Implicaci=C3=B3n de ACQUIRE vs RELEASE: + + Todas las operaciones ACQUIRE emitidas antes de una operaci=C3=B3n RE= LEASE + ser=C3=A1n completadas antes de la operaci=C3=B3n RELEASE. + + (5) Implicaci=C3=B3n de ACQUIRE condicional fallido: + + Ciertas variantes de bloqueo de la operaci=C3=B3n ACQUIRE pueden fall= ar, ya + sea debido a no poder obtener el bloqueo de inmediato, o debido a que + recibieron una se=C3=B1al de desbloqueo mientras dorm=C3=ADan esperan= do que el + cerrojo estuviera disponible. Los fallos en cerrojos no implican + ning=C3=BAn tipo de barrera. + +[!] Nota: una de las consecuencias de que los cerrojos en ACQUIRE y RELEASE +sean barreras unidireccionales, es que los efectos de las instrucciones +fuera de una secci=C3=B3n cr=C3=ADtica pueden filtrarse al interior de la = secci=C3=B3n +cr=C3=ADtica. + +No se puede suponer que un ACQUIRE seguido de una RELEASE sea una barrera +de memoria completa dado que es posible que un acceso anterior a ACQUIRE +suceda despu=C3=A9s del ACQUIRE, y un acceso posterior a la RELEASE suceda= antes +del RELEASE, y los dos accesos puedan entonces cruzarse: + + *A =3D a; + ACQUIRE M + RELEASE M + *B =3D b; + +puede ocurrir como: + + ACQUIRE M, STORE *B, STORE *A, RELEASE M + +Cuando ACQUIRE y RELEASE son bloqueo de adquisici=C3=B3n y liberaci=C3=B3n, +respectivamente, este mismo orden puede ocurrir si el cerrojo ACQUIRE y +RELEASE son para la misma variable de bloqueo, pero solo desde la +perspectiva de otra CPU que no tiene ese bloqueo. En resumen, un ACQUIRE +seguido de un RELEASE NO puede entenderse como una barrera de memoria +completa. + +De manera similar, el caso inverso de un RELEASE seguido de un ACQUIRE no +implica una barrera de memoria completa. Por lo tanto, la ejecuci=C3=B3n d= e la +CPU de los tramos cr=C3=ADticos correspondientes a la RELEASE y la ACQUIRE +pueden cruzarse, de modo que: + + *A =3D a; + RELEASE M + ACQUIRE N + *B =3D b; + +puede ocurrir como: + + ACQUIRE N, STORE *B, STORE *A, RELEASE M + +Podr=C3=ADa parecer que este nuevo orden podr=C3=ADa introducir un punto m= uerto. +Sin embargo, esto no puede suceder porque si tal punto muerto amenazara +con suceder, el RELEASE simplemente se completar=C3=ADa, evitando as=C3=AD= el +interbloqueo ("deadlock", punto muerto). + + =C2=BFPor qu=C3=A9 funciona esto? + + Un punto clave es que solo estamos hablando de la CPU re-haciendo el + orden, no el compilador. Si el compilador (o, ya puestos, el + desarrollador) cambi=C3=B3 las operaciones, un deadlock -podr=C3=ADa- oc= urrir. + + Pero supongamos que la CPU reorden=C3=B3 las operaciones. En este caso, el + desbloqueo precede al bloqueo en el c=C3=B3digo ensamblador. La CPU + simplemente eligi=C3=B3 intentar ejecutar primero la =C3=BAltima operaci= =C3=B3n de + bloqueo. Si hay un interbloqueo, esta operaci=C3=B3n de bloqueo simpleme= nte + esperar=C3=A1 (o tratar=C3=A1 de dormir, pero hablaremos de eso m=C3=A1s= adelante). La + CPU eventualmente ejecutar=C3=A1 la operaci=C3=B3n de desbloqueo (que pr= ecedi=C3=B3 a la + operaci=C3=B3n de bloqueo en el c=C3=B3digo ensamblador), lo que desenmas= car=C3=A1 el + potencial punto muerto, permitiendo que la operaci=C3=B3n de bloqueo ten= ga + =C3=A9xito. + + Pero, =C2=BFy si el cerrojo es un cerrojo que duerme ("sleeplock")? En tal + caso, el c=C3=B3digo intentar=C3=A1 entrar al scheduler, donde eventualm= ente + encontrar=C3=A1 una barrera de memoria, que forzar=C3=A1 la operaci=C3= =B3n de desbloqueo + anterior para completar, nuevamente desentra=C3=B1ando el punto muerto. = Podr=C3=ADa + haber una carrera de desbloqueo del sue=C3=B1o ("sleep-unlock race"), per= o la + primitiva de bloqueo necesita resolver tales carreras correctamente en + cualquier caso. + +Es posible que los cerrojos y los sem=C3=A1foros no proporcionen ninguna +garant=C3=ADa de orden en sistemas compilados en UP, por lo que no se puede +contar con tal situaci=C3=B3n para lograr realmente nada en absoluto, +especialmente con respecto a los accesos de E/S, a menos que se combinen +con operaciones de inhabilitaci=C3=B3n de interrupciones. + +Consulte tambi=C3=A9n la secci=C3=B3n "Efectos de barrera adquiriendo intr= a-CPU". + + +Como ejemplo, considere lo siguiente: + + *A =3D a; + *B =3D b; + ACQUIRE + *C =3D c; + *D =3D d; + RELEASE + *E =3D e; + *F =3D f; + +La siguiente secuencia de eventos es aceptable: + + ACQUIRE, {*F,*A}, *E, {*C,*D}, *B, RELEASE + + [+] Tenga en cuenta que {*F,*A} indica un acceso combinado. + +Pero ninguno de los siguientes lo son: + + {*F,*A}, *B, ACQUIRE, *C, *D, RELEASE, *E + *A, *B, *C, ACQUIRE, *D, RELEASE, *E, *F + *A, *B, ACQUIRE, *C, RELEASE, *D, *E, *F + *B, ACQUIRE, *C, *D, RELEASE, {*F,*A}, *E + + + +FUNCIONES DE DESACTIVACI=C3=93N DE INTERRUPCIONES +-------------------------------------------- + +Las funciones que deshabilitan interrupciones (equivalentes a ACQUIRE) y +habilitan interrupciones (equivalentes a RELEASE) actuar=C3=A1n =C3=BAnica= mente como +barrera del compilador. Por consiguiente, si la memoria o la E/S requieren +barreras en tal situaci=C3=B3n, deben ser provistas por alg=C3=BAn otro me= dio. + + +FUNCIONES DE DORMIR Y DESPERTAR +------------------------------- + +Dormir y despertar son eventos marcados ("flagged") en los datos globales +que se pueden ver como una interacci=C3=B3n entre dos piezas de datos: el = estado +de la task (hilo, proceso, tarea) que espera el evento y los datos globales +utilizados para indicar el evento. Para asegurarse de que estos parezcan +suceder en el orden correcto, las primitivas para comenzar el proceso de ir +a dormir, y las primitivas para iniciar un despertar implican ciertas +barreras. + +En primer lugar, el agente durmiente normalmente sigue algo similar a esta +secuencia de eventos: + + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (evento_indicado) + break; + schedule(); // planificar + } + +Una barrera de memoria general se obtiene autom=C3=A1ticamente mediante +set_current_state() despu=C3=A9s de haber alterado el estado de la tarea: + + CPU 1 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D + set_current_state(); // hacer_estado_actual() + smp_store_mb(); + STORE current->state + + LOAD evento_indicado + +set_current_state() puede estar envuelto por: + + prepare_to_wait(); // preparese_para_esperar(); + prepare_to_wait_exclusive(); // prepararse_para_solo_esperar(); + +que por lo tanto tambi=C3=A9n implican una barrera de memoria general desp= u=C3=A9s de +establecer el estado. Toda la secuencia anterior est=C3=A1 disponible en v= arias +formas, todas las cuales obtienen la barrera de memoria en el lugar +correcto: + + wait_event(); + wait_event_interruptible(); + wait_event_interruptible_exclusive(); + wait_event_interruptible_timeout(); + wait_event_killable(); + wait_event_timeout(); + wait_on_bit(); + wait_on_bit_lock(); + + +En segundo lugar, el c=C3=B3digo que realiza una activaci=C3=B3n normalmen= te se +asemeja a algo como esto: + + evento_indicado =3D 1; + wake_up(&event_wait_queue); // despertar + +o: + + evento_indicado =3D 1; + wake_up_process(event_daemon); // despertar proceso + +wake_up() ejecuta una barrera de memoria general si despierta algo. Si no +despierta nada, entonces una barrera de memoria puede o no ser ejecutada; +no debe confiar en ello. La barrera se produce antes del acceso al estado +de la tarea. En particular, se encuentra entre el STORE para indicar el +evento y el STORE para configurar TASK_RUNNING (hilo ejecutando): + + CPU 1 (Durmiente) CPU 2 (Despertadora) + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + set_current_state(); STORE evento_indicado + smp_store_mb(); wake_up(); + STORE current->state ... + + LOAD evento_indicado if ((LOAD task->state) & TASK_NORMAL) + STORE task->state + +donde "task" es el subproceso que se est=C3=A1 despertando y es igual al +"current" (hilo actual) de la CPU 1. + +Para reiterar, se garantiza la ejecuci=C3=B3n de una barrera de memoria ge= neral +mediante wake_up() si algo est=C3=A1 realmente despierto, pero de lo contr= ario +no existe tal garant=C3=ADa. Para entender esto, considere la siguiente +secuencia de eventos, donde X e Y son ambos cero inicialmente: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + X =3D 1; Y =3D 1; + smp_mb(); wake_up(); + LOAD Y LOAD X + +Si ocurre una reactivaci=C3=B3n ("wakeup"), una (al menos) de las dos carg= as +debe ver 1. Si, por otro lado, no ocurre una reactivaci=C3=B3n, ambas carg= as +pueden ver 0. + +wake_up_process() siempre ejecuta una barrera de memoria general. La +barrera, de nuevo, ocurre antes de que se acceda al estado del hilo. En +particular, si wake_up(), en el fragmento anterior, fuera reemplazado por +una llamada a wake_up_process(), las dos cargas ver=C3=ADan 1, garantizado. + +Las funciones de activaci=C3=B3n disponibles incluyen: + + complete(); + wake_up(); + wake_up_all(); + wake_up_bit(); + wake_up_interruptible(); + wake_up_interruptible_all(); + wake_up_interruptible_nr(); + wake_up_interruptible_poll(); + wake_up_interruptible_sync(); + wake_up_interruptible_sync_poll(); + wake_up_locked(); + wake_up_locked_poll(); + wake_up_nr(); + wake_up_poll(); + wake_up_process(); + +En t=C3=A9rminos de orden de la memoria, todas estas funciones proporciona= n las +mismas garant=C3=ADas que un wake_up() (o m=C3=A1s fuertes). + +[!] Tenga en cuenta que las barreras de la memoria implicadas por el +durmiente y el despierto _no_ ordenan varios stores antes del despertar con +respecto a cargas de los valores guardados despu=C3=A9s de que el durmient= e haya +llamado a set_current_state(). Por ejemplo, si el durmiente hace: + + set_current_state(TASK_INTERRUPTIBLE); + if (evento_indicado) + break; + __set_current_state(TASK_RUNNING); + hacer_algo(my_data); + +y el que despierta hace: + + my_data =3D valor; + evento_indicado =3D 1; + wake_up(&event_wait_queue); + +no existe garant=C3=ADa de que el cambio a event_indicated sea percibido p= or +el durmiente de manera que venga despu=C3=A9s del cambio a my_data. En tal +circunstancia, el c=C3=B3digo en ambos lados debe sacar sus propias barrer= as de +memoria entre los separados accesos a datos. Por lo tanto, el durmiente +anterior deber=C3=ADa hacer: + + set_current_state(TASK_INTERRUPTIBLE); + if (evento_indicado) { + smp_rmb(); + hacer_algo(my_data); + } + +y el que despierta deber=C3=ADa hacer: + + my_data =3D value; + smp_wmb(); + evento_indicado =3D 1; + wake_up(&event_wait_queue); + +FUNCIONES VARIAS +---------------- + +Otras funciones que implican barreras: + + (*) schedule() y similares implican barreras completas de memoria. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +EFECTOS DE BARRERA ADQUIRIENDO INTRA-CPU +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +En los sistemas SMP, las primitivas de bloqueo proveen una forma m=C3=A1s +sustancial de barrera: una que afecta el orden de acceso a la memoria en +otras CPU, dentro del contexto de conflicto en cualquier bloqueo en +particular. + + +ADQUISICI=C3=93N VS ACCESOS A MEMORIA +-------------------------------- + +Considere lo siguiente: el sistema tiene un par de spinlocks (M) y (Q), y +tres CPU; entonces la siguiente secuencia de eventos deber=C3=ADa ocurrir: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + WRITE_ONCE(*A, a); WRITE_ONCE(*E, e); + ACQUIRE M ACQUIRE Q + WRITE_ONCE(*B, b); WRITE_ONCE(*F, f); + WRITE_ONCE(*C, c); WRITE_ONCE(*G, g); + RELEASE M RELEASE Q + WRITE_ONCE(*D, d); WRITE_ONCE(*H, h); + +Entonces no hay garant=C3=ADa sobre en qu=C3=A9 orden ver=C3=A1 la CPU 3 l= os accesos a *A +hasta que *H ocurra, adem=C3=A1s de las restricciones impuestas por los bl= oqueos +separados en las distintas CPUs. Podr=C3=ADa, por ejemplo, ver: + + *E, ACQUIRE M, ACQUIRE Q, *G, *C, *F, *A, *B, RELEASE Q, *D, *H, RELEASE M + +Pero no ver=C3=A1 ninguno de: + + *B, *C or *D preceding ACQUIRE M + *A, *B or *C following RELEASE M + *F, *G or *H preceding ACQUIRE Q + *E, *F or *G following RELEASE Q + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +=C2=BFD=C3=93NDE SE NECESITAN BARRERAS DE MEMORIA? +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Bajo operaci=C3=B3n normal, el re-ordenamiento de una operaci=C3=B3n de me= moria +generalmente no va a suponer un problema, ya que para una pieza de c=C3=B3= digo +lineal de un solo subproceso seguir=C3=A1 pareciendo que funciona correcta= mente, +incluso si est=C3=A1 en un kernel SMP. Existen, sin embargo, cuatro +circunstancias en las que reordenar definitivamente _podr=C3=ADa_ ser un +problema: + + (*) Interacci=C3=B3n entre procesadores. + + (*) Operaciones at=C3=B3micas. + + (*) Acceso a dispositivos. + + (*) Interrupciones. + + +INTERACCI=C3=93N ENTRE PROCESADORES +------------------------------ + +Cuando se da un sistema con m=C3=A1s de un procesador, m=C3=A1s de una CPU= en el +sistema puede estar trabajando en el mismo conjunto de datos al mismo +tiempo. Esto puede causar problemas de sincronizaci=C3=B3n, y la forma hab= itual +de tratar con estos es utilizar cerrojos. Sin embargo, los cerrojos son +bastante caros, por lo que puede ser preferible operar sin el uso de un +cerrojo a ser posible. En cuyo caso, es posible que las operaciones que +afectan a ambas CPU deban ordenarse cuidadosamente para evitar un +funcionamiento incorrecto. + +Considere, por ejemplo, la ruta lenta del sem=C3=A1foro R/W. Aqu=C3=AD hay= un proceso +de espera en cola del sem=C3=A1foro, en virtud de que tiene una parte de s= u pila +vinculada a la lista de procesos en espera del sem=C3=A1foro: + + struct rw_semaphore { + ... + spinlock_t lock; + struct list_head waiters; + }; + + struct rwsem_waiter { + struct list_head list; + struct task_struct *task; + }; + +Para despertar a un proceso que espera ("waiter") en particular, las +funciones up_read() o up_write() tienen que: + + (1) leer el siguiente puntero del registro de este proceso que espera, + para saber d=C3=B3nde est=C3=A1 el registro del siguiente waiter; + + (2) leer el puntero a la estructura de tareas del waiter; + + (3) borrar el puntero de la tarea para decirle al waiter que se le ha dado + el sem=C3=A1foro; + + (4) llamar a wake_up_process() en la tarea; y + + (5) liberar la referencia retenida en la estructura de tareas del waiter. + +En otras palabras, tiene que realizar esta secuencia de eventos: + + LOAD waiter->list.next; + LOAD waiter->task; + STORE waiter->task; + CALL wakeup + RELEASE task + +y si alguno de estos pasos ocurre fuera de orden, entonces todo puede que +funcione defectuosamente. + +Una vez que se ha puesto en cola y soltado el bloqueo de sem=C3=A1foro, el +proceso que espera no consigue el candado de nuevo; en cambio, solo espera +a que se borre su puntero de tarea antes de continuar. Dado que el registro +est=C3=A1 en la pila del proceso que espera, esto significa que si el punt= ero de +la tarea se borra _antes_ de que se lea el siguiente puntero de la lista, +otra CPU podr=C3=ADa comenzar a procesar el proceso que espera y podr=C3= =ADa romper +el stack del proceso que espera antes de que la funci=C3=B3n up*() tenga la +oportunidad de leer el puntero que sigue. + +Considere entonces lo que podr=C3=ADa suceder con la secuencia de eventos +anterior: + + CPU 1 CPU 2 + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + down_xxx() + Poner waiter en la "queue" (cola) + Dormir + up_yyy() + LOAD waiter->task; + STORE waiter->task; + Despertado por otro evento + + Reanudar el procesamiento + down_xxx() regresa + llamada a foo() + foo() estropea *waiter + + LOAD waiter->list.next; + --- OOPS --- + +Esto podr=C3=ADa solucionarse usando el bloqueo de sem=C3=A1foro, pero lue= go la +funci=C3=B3n down_xxx() tiene que obtener innecesariamente el spinlock +nuevamente, despu=C3=A9s de ser despertado el hilo. + +La forma de lidiar con esto es insertar una barrera de memoria SMP general: + + LOAD waiter->list.next; + LOAD waiter->task; + smp_mb(); + STORE waiter->task; + CALL wakeup + RELEASE task + +En este caso, la barrera garantiza que todos los accesos a memoria antes de +la barrera parecer=C3=A1n suceder antes de todos los accesos a memoria des= pu=C3=A9s +de dicha barrera con respecto a las dem=C3=A1s CPU del sistema. _No_ garan= tiza +que todos los accesos a memoria antes de la barrera se completar=C3=A1n en= el +momento en que la instrucci=C3=B3n de la barrera en s=C3=AD se complete. + +En un sistema UP, donde esto no ser=C3=ADa un problema, la funci=C3=B3n sm= p_mb() es +solo una barrera del compilador, asegur=C3=A1ndose as=C3=AD de que el comp= ilador +emita las instrucciones en el orden correcto sin realmente intervenir en la +CPU. Como solo hay un CPU, la l=C3=B3gica de orden de dependencias de esa = CPU se +encargar=C3=A1 de todo lo dem=C3=A1s. + + +OPERACIONES AT=C3=93MICAS +-------------------- + +Si bien son, t=C3=A9cnicamente, consideraciones de interacci=C3=B3n entre +procesadores, las operaciones at=C3=B3micas se destacan especialmente porq= ue +algunas de ellas implican barreras de memoria completa y algunas otras no, +pero se conf=C3=ADa mucho en ellos en su conjunto a lo largo del kernel. + +Consulte Documentation/atomic_t.txt para obtener m=C3=A1s informaci=C3=B3n. + + +ACCESO A DISPOSITIVOS +--------------------- + +Un driver puede ser interrumpido por su propia rutina de servicio de +interrupci=C3=B3n y, por lo tanto, las dos partes del driver pueden interf= erir +con los intentos de controlar o acceder al dispositivo. + +Esto puede aliviarse, al menos en parte, desactivando las interrupciones +locales (una forma de bloqueo), de modo que las operaciones cr=C3=ADticas = sean +todas contenidas dentro la secci=C3=B3n de interrupci=C3=B3n desactivada e= n el +controlador. Mientras la interrupci=C3=B3n del driver est=C3=A1 ejecutando= la rutina, +es posible que el "core" del controlador no se ejecute en la misma CPU y no +se permita que su interrupci=C3=B3n vuelva a ocurrir hasta que la interrup= ci=C3=B3n +actual haya sido resuelta, por lo tanto, el controlador de interrupci=C3= =B3n no +necesita bloquearse contra esto. + +Sin embargo, considere un driver que estaba hablando con una tarjeta +ethernet que tiene un registro de direcciones y un registro de datos. Si +el core de ese controlador habla con la tarjeta estando en desactivaci=C3= =B3n de +interrupci=C3=B3n y luego se invoca el controlador de interrupci=C3=B3n del +controlador: + + IRQ LOCALES DESACTIVADAS + writew(ADDR, 3); + writew(DATA, y); + IRQ LOCALES ACTIVADAS + + writew(ADDR, 4); + q =3D readw(DATA); + + +El almacenamiento en el registro de datos puede ocurrir despu=C3=A9s del s= egundo +almacenamiento en el registro de direcciones si las reglas de orden son lo +suficientemente relajadas: + + STORE *ADDR =3D 3, STORE *ADDR =3D 4, STORE *DATA =3D y, q =3D LOAD *DA= TA + +Si se relajan las reglas de orden, se debe asumir que los accesos +realizados dentro de la secci=C3=B3n con interrupci=C3=B3n deshabilitada p= ueden +filtrarse fuera de esta y pueden intercalarse con accesos realizados en una +interrupci=C3=B3n - y viceversa - a menos que se utilicenn barreras impl= =C3=ADcita o +expl=C3=ADcitas. + +Normalmente, esto no ser=C3=A1 un problema porque los accesos de E/S reali= zados +dentro de tales secciones incluir=C3=A1n operaciones de carga s=C3=ADncron= as en +registros E/S estrictamente ordenados, que forman barreras de E/S +impl=C3=ADcitas. + + +Una situaci=C3=B3n similar puede ocurrir entre una rutina de interrupci=C3= =B3n y dos +rutinas ejecut=C3=A1ndose en separadas CPU que se comunican entre s=C3=AD.= Si tal +caso es probable, entonces se deben usar bloqueos de desactivaci=C3=B3n de +interrupciones para garantizar el orden. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + Efectos de barrera de E/S del kernel +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +La interfaz con perif=C3=A9ricos a trav=C3=A9s de accesos de E/S es profun= damente +espec=C3=ADfica para cada arquitectura y dispositivo. Por lo tanto, los dr= ivers +que son inherentemente no port=C3=A1tiles pueden depender de comportamient= os +espec=C3=ADficos de sus sistemas de destino, con el fin de lograr la +sincronizaci=C3=B3n de la manera m=C3=A1s ligera posible. Para drivers que= deseen ser +port=C3=A1tiles entre m=C3=BAltiples arquitecturas e implementaciones de b= us, el +kernel ofrece una serie de funciones de acceso que proporcionan varios +grados de garant=C3=ADas de orden: + + (*) readX(), writeX(): + + Las funciones de acceso MMIO readX() y writeX() usan un puntero al + perif=C3=A9rico al que se accede como un par=C3=A1metro __iomem *. para p= unteros + asignados los atributos de E/S predeterminados (por ejemplo, los + devueltos por ioremap()), las garant=C3=ADas de orden son las sigu= ientes: + + 1. Se ordenan todos los accesos readX() y writeX() a un mismo perif=C3=A9= rico + entre estos. Esto asegura que los registros de acceso MMIO por el + mismo subproceso de la CPU a un dispositivo en particular llegar=C3=A1= n en + el orden del programa. + + 2. Se ordena un writeX() emitido por un subproceso de CPU que contiene un + spinlock antes de un writeX() al mismo perif=C3=A9rico desde otro + subproceso de CPU, si emitido despu=C3=A9s de una adquisici=C3= =B3n posterior del + mismo spinlock. Esto garantiza que ese registro MMIO escribe en un + dispositivo en particular, mientras que se obtiene un spinlock = en un + orden consistente con las adquisiciones del cerrojo. + + 3. Un writeX() por un subproceso de la CPU al perif=C3=A9rico primero esp= erar=C3=A1 + a la finalizaci=C3=B3n de todas las escrituras anteriores en la memoria + emitidas por, o bien propagadas por, el mismo subproceso. Esto = asegura + que las escrituras de la CPU a un b=C3=BAfer DMA de salida asignadas p= or + dma_alloc_coherent() ser=C3=A1n visibles para un motor ("engine= ") DMA + cuando la CPU escriba en sus registros de control MMIO, para ac= tivar + la transferencia. + + 4. Un readX() de un subproceso del CPU, desde el perif=C3=A9rico, se + completar=C3=A1 antes de que cualquier lectura subsiguiente de memoria= por + el mismo subproceso pueda comenzar. Esto asegura que las lecturas de + la CPU desde un b=C3=BAfer DMA entrantes asignadas por + dma_alloc_coherent(), no ver=C3=A1n datos obsoletos despu=C3=A9= s de leer el + registro de estado MMIO del motor DMA, para establecer que la + transferencia DMA se haya completado. + + 5. Un readX() por un subproceso del CPU, desde el perif=C3=A9rico, se + completar=C3=A1 antes de que cualquier bucle delay() subsiguiente pueda + comenzar a ejecutarse en el mismo subproceso. Esto asegura que = dos + escrituras del CPU a registros MMIO en un perif=C3=A9rico llega= r=C3=A1n al menos + con 1us de diferencia, si la primera escritura se lee inmediatamente + de vuelta con readX() y se llama a udelay(1) antes del segundo + writeX(): + + writel(42, DEVICE_REGISTER_0); // Llega al dispositivo ... + readl(DEVICE_REGISTER_0); + udelay(1); + writel(42, DEVICE_REGISTER_1); // al menos 1us antes de esto.... + +Las propiedades de orden de los punteros __iomem obtenidos con valores de +atributos que no sean los valores por defecto (por ejemplo, los devueltos +por ioremap_wc()) son espec=C3=ADficos de la arquitectura subyacente y, po= r lo +tanto, las garant=C3=ADas enumeradas anteriormente no pueden por lo genera= l ser +aseguradas para accesos a este tipo de "mappings" (asignaciones). + + (*) readX_relaxed(), writeX_relaxed(): + + Son similares a readX() y writeX(), pero proporcionan una garant=C3=ADa de + orden de memoria m=C3=A1s d=C3=A9bil. Espec=C3=ADficamente, no gar= antizan orden con + respecto al bloqueo, los accesos normales a la memoria o los bucles + delay() (es decir, los puntos 2-5 arriba) pero todav=C3=ADa se gar= antiza que + se ordenar=C3=A1n con respecto a otros accesos desde el mismo hilo= de la CPU, + al mismo perif=C3=A9rico, cuando se opera en punteros __iomem asig= nados con el + valor predeterminado para los atributos de E/S. + + (*) readsX(), writesX(): + + Los puntos de entrada readsX() y writesX() MMIO est=C3=A1n dise=C3=B1ados= para + acceder FIFOs mapeados en memoria y basados en registros que resid= en en + perif=C3=A9ricos, que no son capaces de realizar DMA. Por tanto, s= =C3=B3lo + proporcionan garant=C3=ADas de orden readX_relaxed() y writeX_rela= xed(), como + se document=C3=B3 anteriormente. + + (*) inX(), outX(): + + Los puntos de entrada inX() y outX() est=C3=A1n destinados a acceder a ma= pas + de puertos "legacy" (antiguos) de perif=C3=A9ricos de E/S, que pue= den requerir + instrucciones especiales en algunas arquitecturas (especialmente, en + x86). El n=C3=BAmero de puerto del perif=C3=A9rico que se est=C3= =A1 accedido se pasa + como un argumento. + + Dado que muchas arquitecturas de CPU acceden finalmente a estos + perif=C3=A9ricos a trav=C3=A9s de un mapeo interno de memoria virt= ual, las + garant=C3=ADas de orden port=C3=A1tiles proporcionadas por inX() y= outX() son las + mismas que las proporcionadas por readX() y writeX(), respectivame= nte, al + acceder a una asignaci=C3=B3n con los valores de atributos de E/S + predeterminados (los que haya por defecto). + + Los drivers de dispositivos pueden esperar que outX() emita una + transacci=C3=B3n de escritura no publicada, que espera una respues= ta de + finalizaci=C3=B3n del perif=C3=A9rico de E/S antes de regresar. Es= to no est=C3=A1 + garantizado por todas las arquitecturas y por lo tanto no forma pa= rte de + la sem=C3=A1ntica de orden port=C3=A1til. + + (*) insX(), outsX(): + + Como arriba, los puntos de entrada insX() y outsX() proporcionan e= l mismo + orden garantizado por readsX() y writesX() respectivamente, al acc= eder a + un mapping con los atributos de E/S predeterminados. + + (*) ioreadX(), iowriteX(): + + Estos funcionar=C3=A1n adecuadamente para el tipo de acceso que re= almente est=C3=A1n + haciendo, ya sea inX()/outX() o readX()/writeX(). + +Con la excepci=C3=B3n de los puntos de entrada (insX(), outsX(), readsX() y +writesX()), todo lo anterior supone que el perif=C3=A9rico subyacente es +little-endian y, por lo tanto, realizar=C3=A1 operaciones de intercambio de +bytes en arquitecturas big-endian. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +MODELO DE ORDEN M=C3=8DNIMO DE EJECUCI=C3=93N ASUMIDO +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Debe suponerse que la CPU conceptual est=C3=A1 d=C3=A9bilmente ordenada, p= ero que +mantiene la apariencia de causalidad del programa con respecto a s=C3=AD m= isma. +Algunas CPU (como i386 o x86_64) est=C3=A1n m=C3=A1s limitadas que otras (= como +powerpc o frv), por lo que el caso m=C3=A1s relajado (es decir, DEC Alpha)= se +debe asumir fuera de c=C3=B3digo espec=C3=ADfico de arquitectura. + +Esto significa que se debe considerar que la CPU ejecutar=C3=A1 su flujo de +instrucciones en el orden que se quiera - o incluso en paralelo - siempre +que si una instrucci=C3=B3n en el flujo depende de una instrucci=C3=B3n an= terior, +entonces dicha instrucci=C3=B3n anterior debe ser lo suficientemente compl= eta[*] +antes de que la posterior instrucci=C3=B3n puede proceder; en otras palabr= as: +siempre que la apariencia de causalidad se mantenga. + + [*] Algunas instrucciones tienen m=C3=A1s de un efecto, como cambiar el + c=C3=B3digo de condici=C3=B3n, cambio de registros o cambio de memori= a - y + distintas instrucciones pueden depender de diferentes efectos. + +Una CPU puede tambi=C3=A9n descartar cualquier secuencia de instrucciones = que +termine sin tener efecto final. Por ejemplo, si dos instrucciones +adyacentes cargan un valor inmediato en el mismo registro, la primera puede +descartarse. + + +De manera similar, se debe suponer que el compilador podr=C3=ADa reordenar= la +corriente de instrucciones de la manera que crea conveniente, nuevamente +siempre que la apariencia de causalidad se mantenga. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +EFECTOS DE LA MEMORIA CACH=C3=89 DE LA CPU +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +La forma en que se perciben las operaciones de memoria cach=C3=A9 en todo = el +sistema se ve afectada, hasta cierto punto, por los cach=C3=A9s que se +encuentran entre las CPU y la memoria, y por el sistema de coherencia en +memoria que mantiene la consistencia de estado en el sistema. + +En cuanto a la forma en que una CPU interact=C3=BAa con otra parte del sis= tema a +trav=C3=A9s del cach=C3=A9, el sistema de memoria tiene que incluir los ca= ch=C3=A9s de la +CPU y barreras de memoria, que en su mayor parte act=C3=BAan en la interfaz +entre la CPU y su cach=C3=A9 (las barreras de memoria l=C3=B3gicamente act= =C3=BAan sobre +la l=C3=ADnea de puntos en el siguiente diagrama): + + <--- CPU ---> : <----------- Memoria -----------> + : + +--------+ +--------+ : +--------+ +-----------+ + | Core | | Cola | : | Cache | | | +---------+ + | CPU | | de | : | CPU | | | | | + | |--->| acceso |----->| |<-->| | | | + | | | a | : | | | |--->| Memoria | + | | | memoria| : | | | | | | + +--------+ +--------+ : +--------+ | Mecanismo | | | + : | de | +---------+ + : | Coherencia| + : | de la | +--------+ + +--------+ +--------+ : +--------+ | cache | | | + | Core | | Cola | : | Cache | | | | | + | CPU | | de | : | CPU | | |--->| Dispos | + | |--->| acceso |----->| |<-->| | | itivo | + | | | a | : | | | | | | + | | | memoria| : | | | | +--------+ + +--------+ +--------+ : +--------+ +-----------+ + : + : + +Aunque es posible que una carga o store en particular no aparezca fuera de +la CPU que lo emiti=C3=B3, ya que puede haber sido satisfecha dentro del p= ropio +cach=C3=A9 de la CPU, seguir=C3=A1 pareciendo como si el acceso total a la= memoria +hubiera tenido lugar para las otras CPUs, ya que los mecanismos de +coherencia de cach=C3=A9 migrar=C3=A1n la cacheline sobre la CPU que acced= e y se +propagar=C3=A1n los efectos en caso de conflicto. + +El n=C3=BAcleo de la CPU puede ejecutar instrucciones en el orden que cons= idere +adecuado, siempre que parezca mantenerse la causalidad esperada del +programa. Algunas de las instrucciones generan operaciones de carga y +almacenamiento que luego van a la cola de accesos a memoria a realizar. El +n=C3=BAcleo puede colocarlos en la cola en cualquier orden que desee, y +continuar su ejecuci=C3=B3n hasta que se vea obligado a esperar que una +instrucci=C3=B3n sea completada. + +De lo que se ocupan las barreras de la memoria es de controlar el orden en +que los accesos cruzan, desde el lado de la CPU, hasta el lado de memoria, +y el orden en que los otros observadores perciben los efectos en el sistema +que sucedan por esto. + +[!] Las barreras de memoria _no_ son necesarias dentro de una CPU +determinada, ya que las CPU siempre ven sus propias cargas y stores como si +hubieran sucedido en el orden del programa. + +[!] Los accesos a MMIO u otros dispositivos pueden pasar por alto el +sistema de cach=C3=A9. Esto depende de las propiedades de la ventana de me= moria +a trav=C3=A9s de la cual se accede a los dispositivos y/o el uso de +instrucciones especiales de comunicaci=C3=B3n con dispositivo que pueda te= ner la +CPU. + + +COHERENCIA DE CACH=C3=89 FRENTE A DMA +--------------------------------- + +No todos los sistemas mantienen coherencia de cach=C3=A9 con respecto a los +dispositivos que realizan DMA. En tales casos, un dispositivo que intente +DMA puede obtener datos obsoletos de la RAM, porque las l=C3=ADneas de cac= h=C3=A9 +"sucias" pueden residir en los cach=C3=A9s de varias CPU, y es posible que= no +se hayan vuelto a escribir en la RAM todav=C3=ADa. Para hacer frente a est= o, la +parte apropiada del kernel debe vaciar los bits superpuestos de cach=C3=A9= en +cada CPU (y tal vez tambi=C3=A9n invalidarlos). + +Adem=C3=A1s, los datos enviados por DMA a RAM, por un dispositivo, pueden = ser +sobrescritos por l=C3=ADneas de cach=C3=A9 sucias que se escriben de nuevo= en la RAM +desde el cach=C3=A9 de una CPU, despu=C3=A9s de que el dispositivo haya pu= esto sus +propios datos, o las l=C3=ADneas de cach=C3=A9 presentes en el cach=C3=A9 = de la CPU pueden +simplemente ocultar el hecho de que la memoria RAM se haya actualizado, +hasta el momento en que la cach=C3=A9 se descarta de la memoria cach=C3=A9= de la CPU +y se vuelve a cargar. Para hacer frente a esto, la parte apropiada del +kernel debe invalidar los bits superpuestos del cach=C3=A9 en cada CPU. + +Consulte Documentation/core-api/cachetlb.rst para obtener m=C3=A1s informa= ci=C3=B3n +sobre administraci=C3=B3n de la memoria cach=C3=A9. + + +COHERENCIA DE CACH=C3=89 FRENTE A MMIO +--------------------------------- + +La E/S mapeada en memoria generalmente se lleva a cabo a trav=C3=A9s de +ubicaciones de memoria que forman parte de una ventana del espacio de +memoria de la CPU, que tiene diferentes propiedades asignadas que la +ventana habitual dirigida a RAM. + +Entre dichas propiedades, suele existir el hecho de que tales accesos +eluden el almacenamiento en cach=C3=A9 por completo e ir directamente a los +buses del dispositivo. Esto significa que los accesos MMIO pueden, en +efecto, superar los accesos a la memoria cach=C3=A9 que se emitieron +anteriormente. Una barrera de memoria no es suficiente en tal caso, sino +que el cach=C3=A9 debe ser vaciado entre la escritura de la memoria cach= =C3=A9, y el +acceso MMIO, si los dos son de cualquier manera dependiente. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +COSAS QUE HACEN LAS CPU +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Un programador podr=C3=ADa dar por sentado que la CPU realizar=C3=A1 las o= peraciones +de memoria exactamente en el orden especificado, de modo que si a la CPU se +entrega, por ejemplo, el siguiente fragmento de c=C3=B3digo a ejecutar: + + a =3D READ_ONCE(*A); + WRITE_ONCE(*B, b); + c =3D READ_ONCE(*C); + d =3D READ_ONCE(*D); + WRITE_ONCE(*E, e); + +esperar=C3=ADan entonces que la CPU complete la operaci=C3=B3n de memoria = para cada +instrucci=C3=B3n antes de pasar a la siguiente, lo que lleva a una definida +secuencia de operaciones vistas por observadores externos en el sistema: + + LOAD *A, STORE *B, LOAD *C, LOAD *D, STORE *E. + +La realidad es, por supuesto, mucho m=C3=A1s intrincada. Para muchas CPU y +compiladores, la anterior suposici=C3=B3n no se sostiene porque: + + (*) es m=C3=A1s probable que las cargas deban completarse de inmediato pa= ra + permitir progreso en la ejecuci=C3=B3n, mientras que los stores a men= udo se + pueden aplazar sin problema; + + (*) las cargas se pueden hacer especulativamente, y el resultado es + descartado si resulta innecesario; + + (*) las cargas se pueden hacer de forma especulativa, lo que lleva a que + se haya obtenido el resultado en el momento equivocado de la secuencia + de eventos esperada; + + (*) el orden de los accesos a memoria se puede reorganizar para promover + un mejor uso de los buses y cach=C3=A9s de la CPU; + + (*) las cargas y los stores se pueden combinar para mejorar el rendimiento + cuando se habla con memoria o hardware de E/S, que puede realizar + accesos por lotes a ubicaciones adyacentes, reduciendo as=C3=AD los c= ostes + de configuraci=C3=B3n de transacciones (la memoria y los dispositivos= PCI + pueden ambos pueden hacer esto); y + + (*) la cach=C3=A9 de datos de la CPU puede afectar al orden, y mientras s= us + mecanismos de coherencia pueden aliviar esto, una vez que el store + haya accedido al cach=C3=A9- no hay garant=C3=ADa de que la gesti=C3= =B3n de la + coherencia se propague en orden a otras CPU. + +Entonces, digamos que lo que otra CPU podr=C3=ADa observar en el fragmento= de +c=C3=B3digo anterior es: + + LOAD *A, ..., LOAD {*C,*D}, STORE *E, STORE *B + + (Donde "LOAD {*C,*D}" es una carga combinada) + + +Sin embargo, se garantiza que una CPU es autoconsistente: ver=C3=A1 que sus + _propios_ accesos parecen estar correctamente ordenados, sin necesidad de +barrera de memoria. Por ejemplo con el siguiente c=C3=B3digo: + + U =3D READ_ONCE(*A); + WRITE_ONCE(*A, V); + WRITE_ONCE(*A, W); + X =3D READ_ONCE(*A); + WRITE_ONCE(*A, Y); + Z =3D READ_ONCE(*A); + +y asumiendo que no hay intervenci=C3=B3n de una influencia externa, se pue= de +suponer que el resultado final se parecer=C3=A1 a: + + U =3D=3D el valor original de *A + X =3D=3D W + Z =3D=3D Y + *A =3D=3D Y + +El c=C3=B3digo anterior puede hacer que la CPU genere la secuencia complet= a de +accesos de memoria: + + U=3DLOAD *A, STORE *A=3DV, STORE *A=3DW, X=3DLOAD *A, STORE *A=3DY, Z=3DL= OAD *A + +en ese orden, pero, sin intervenci=C3=B3n, la secuencia puede contener casi +cualquier combinaci=C3=B3n de elementos combinados o descartados, siempre = que la +perspectiva del programa del mundo siga siendo consistente. Tenga en cuenta +que READ_ONCE() y WRITE_ONCE() -no- son opcionales en el ejemplo anterior, +ya que hay arquitecturas donde una CPU determinada podr=C3=ADa reordenar c= argas +sucesivas en la misma ubicaci=C3=B3n. En tales arquitecturas, READ_ONCE() y +WRITE_ONCE() hacen lo que sea necesario para evitar esto, por ejemplo, en +Itanium los casts vol=C3=A1tiles utilizados por READ_ONCE() y WRITE_ONCE()= hacen +que GCC emita las instrucciones especiales ld.acq y st.rel +(respectivamente) que impiden dicha reordenaci=C3=B3n. + +El compilador tambi=C3=A9n puede combinar, descartar o diferir elementos d= e la +secuencia antes incluso de que la CPU los vea. + +Por ejemplo: + + *A =3D V; + *A =3D W; + +puede reducirse a: + + *A =3D W; + +ya que, sin una barrera de escritura o WRITE_ONCE(), puede que se asuma +que el efecto del almacenamiento de V a *A se pierde. Similarmente: + + *A =3D Y; + Z =3D *A; + +puede, sin una barrera de memoria o un READ_ONCE() y WRITE_ONCE(), esto +sea reducido a: + + *A =3D Y; + Z =3D Y; + +y la operaci=C3=B3n LOAD nunca aparezca fuera de la CPU. + + +Y LUEGO EST=C3=81 EL ALFA +-------------------- + +La CPU DEC Alpha es una de las CPU m=C3=A1s relajadas que existen. No solo= eso, +algunas versiones de la CPU Alpha tienen un cach=C3=A9 de datos dividido, = lo que +les permite tener dos l=C3=ADneas de cach=C3=A9 relacionadas sem=C3=A1ntic= amente, +actualizadas en momentos separados. Aqu=C3=AD es donde la barrera de depen= dencia +de direcci=C3=B3n realmente se vuelve necesaria, ya que se sincronizan amb= os +cach=C3=A9s con el sistema de coherencia de memoria, lo que hace que parez= ca un +cambio en el puntero, frente a que los nuevos datos se produzcan en el +orden correcto. + +Alpha define el modelo de memoria del kernel Linux, aunque a partir de +v4.15, la adici=C3=B3n al kernel de Linux de smp_mb() a READ_ONCE() en Alp= ha +redujo en gran medida su impacto en el modelo de memoria. + + +GUESTS DE M=C3=81QUINAS VIRTUALES +----------------------------- + +Los "guests" (invitados) que se ejecutan en m=C3=A1quinas virtuales pueden= verse +afectados por los efectos de SMP incluso si el "host" (hu=C3=A9sped) en s= =C3=AD se +compila sin compatibilidad con SMP. Este es un efecto de la interacci=C3= =B3n con +un host SMP mientras ejecuta un kernel UP. El uso obligatorio de barreras +para este caso de uso ser=C3=ADa posible, pero a menudo no son =C3=B3ptima= s. + +Para hacer frente a este caso de manera =C3=B3ptima, est=C3=A1n disponible= s macros de +bajo nivel virt_mb() etc. Estas tienen el mismo efecto que smp_mb(), etc. +cuando SMP est=C3=A1 habilitado, pero generan c=C3=B3digo id=C3=A9ntico pa= ra sistemas SMP +y no SMP. Por ejemplo, los invitados de m=C3=A1quinas virtuales deber=C3= =ADa usar +virt_mb() en lugar de smp_mb() al sincronizar contra un (posiblemente SMP) +anfitri=C3=B3n. + +Estos son equivalentes a sus contrapartes smp_mb() etc. en todos los dem= =C3=A1s +aspectos, en particular, no controlan los efectos MMIO: para controlar los +efectos MMIO, utilice barreras obligatorias. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +EJEMPLOS DE USOS +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +BUFFERS CIRCULARES +------------------ + +Las barreras de memoria se pueden utilizar para implementar almacenamiento +en b=C3=BAfer circular, sin necesidad de un cerrojo para serializar al pro= ductor +con el consumidor. Vea: + + Documentation/core-api/circular-buffers.rst + +para m=C3=A1s detalles. + + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +REFERENCIAS +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Alpha AXP Architecture Reference Manual, Segunda Edici=C3=B3n (por Sites &= Witek, +Digital Press) + Cap=C3=ADtulo 5.2: Physical Address Space Characteristics + Cap=C3=ADtulo 5.4: Caches and Write Buffers + Cap=C3=ADtulo 5.5: Data Sharing + Cap=C3=ADtulo 5.6: Read/Write Ordering + +AMD64 Architecture Programmer's Manual Volumen 2: System Programming + Cap=C3=ADtulo 7.1: Memory-Access Ordering + Cap=C3=ADtulo 7.4: Buffering and Combining Memory Writes + +ARM Architecture Reference Manual (ARMv8, for ARMv8-A architecture profile) + Cap=C3=ADtulo B2: The AArch64 Application Level Memory Model + +IA-32 Intel Architecture Software Developer's Manual, Volumen 3: +System Programming Guide + Cap=C3=ADtulo 7.1: Locked Atomic Operations + Cap=C3=ADtulo 7.2: Memory Ordering + Cap=C3=ADtulo 7.4: Serializing Instructions + +The SPARC Architecture Manual, Version 9 + Cap=C3=ADtulo 8: Memory Models + Appendix D: Formal Specification of the Memory Models + Appendix J: Programming with the Memory Models + +Storage in the PowerPC (por Stone and Fitzgerald) + +UltraSPARC Programmer Reference Manual + Cap=C3=ADtulo 5: Memory Accesses and Cacheability + Cap=C3=ADtulo 15: Sparc-V9 Memory Models + +UltraSPARC III Cu User's Manual + Cap=C3=ADtulo 9: Memory Models + +UltraSPARC IIIi Processor User's Manual + Cap=C3=ADtulo 8: Memory Models + +UltraSPARC Architecture 2005 + Cap=C3=ADtulo 9: Memory + Appendix D: Formal Specifications of the Memory Models + +UltraSPARC T1 Supplement to the UltraSPARC Architecture 2005 + Cap=C3=ADtulo 8: Memory Models + Appendix F: Caches and Cache Coherency + +Solaris Internals, Core Kernel Architecture, p63-68: + Cap=C3=ADtulo 3.3: Hardware Considerations for Locks and + Synchronization + +Unix Systems for Modern Architectures, Symmetric Multiprocessing and Cachi= ng +for Kernel Programmers: + Cap=C3=ADtulo 13: Other Memory Models + +Intel Itanium Architecture Software Developer's Manual: Volumen 1: + Secci=C3=B3n 2.6: Speculation + Secci=C3=B3n 4.4: Memory Access diff --git a/Documentation/translations/sp_SP/wrappers/memory-barriers.rst = b/Documentation/translations/sp_SP/wrappers/memory-barriers.rst new file mode 100644 index 000000000000..50715b7d51b9 --- /dev/null +++ b/Documentation/translations/sp_SP/wrappers/memory-barriers.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0 + This is a simple wrapper to bring memory-barriers.txt (Spanish + translation) into the RST world until such a time as that file can be + converted directly. + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Barreras de Memoria del kernel Linux +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +.. raw:: latex + + \footnotesize + +.. include:: ../memory-barriers.txt + :literal: + +.. raw:: latex + + \normalsize --=20 2.34.1