From ace9429bb58fd418f0c81d4c2835699bddf6bde6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:27:49 +0200 Subject: Adding upstream version 6.6.15. Signed-off-by: Daniel Baumann --- Documentation/translations/sp_SP/disclaimer-sp.rst | 6 + Documentation/translations/sp_SP/howto.rst | 617 ++++ Documentation/translations/sp_SP/index.rst | 81 + .../translations/sp_SP/memory-barriers.txt | 3134 ++++++++++++++++++++ .../translations/sp_SP/process/adding-syscalls.rst | 632 ++++ .../translations/sp_SP/process/code-of-conduct.rst | 97 + .../translations/sp_SP/process/coding-style.rst | 1315 ++++++++ .../sp_SP/process/contribution-maturity-model.rst | 120 + .../translations/sp_SP/process/deprecated.rst | 381 +++ .../translations/sp_SP/process/email-clients.rst | 374 +++ Documentation/translations/sp_SP/process/index.rst | 24 + .../translations/sp_SP/process/kernel-docs.rst | 187 ++ .../sp_SP/process/kernel-enforcement-statement.rst | 174 ++ .../translations/sp_SP/process/magic-number.rst | 89 + .../sp_SP/process/programming-language.rst | 53 + .../sp_SP/process/researcher-guidelines.rst | 150 + .../sp_SP/process/submitting-patches.rst | 894 ++++++ .../sp_SP/wrappers/memory-barriers.rst | 19 + 18 files changed, 8347 insertions(+) create mode 100644 Documentation/translations/sp_SP/disclaimer-sp.rst create mode 100644 Documentation/translations/sp_SP/howto.rst create mode 100644 Documentation/translations/sp_SP/index.rst create mode 100644 Documentation/translations/sp_SP/memory-barriers.txt create mode 100644 Documentation/translations/sp_SP/process/adding-syscalls.rst create mode 100644 Documentation/translations/sp_SP/process/code-of-conduct.rst create mode 100644 Documentation/translations/sp_SP/process/coding-style.rst create mode 100644 Documentation/translations/sp_SP/process/contribution-maturity-model.rst create mode 100644 Documentation/translations/sp_SP/process/deprecated.rst create mode 100644 Documentation/translations/sp_SP/process/email-clients.rst create mode 100644 Documentation/translations/sp_SP/process/index.rst create mode 100644 Documentation/translations/sp_SP/process/kernel-docs.rst create mode 100644 Documentation/translations/sp_SP/process/kernel-enforcement-statement.rst create mode 100644 Documentation/translations/sp_SP/process/magic-number.rst create mode 100644 Documentation/translations/sp_SP/process/programming-language.rst create mode 100644 Documentation/translations/sp_SP/process/researcher-guidelines.rst create mode 100644 Documentation/translations/sp_SP/process/submitting-patches.rst create mode 100644 Documentation/translations/sp_SP/wrappers/memory-barriers.rst (limited to 'Documentation/translations/sp_SP') diff --git a/Documentation/translations/sp_SP/disclaimer-sp.rst b/Documentation/translations/sp_SP/disclaimer-sp.rst new file mode 100644 index 0000000000..a400034e95 --- /dev/null +++ b/Documentation/translations/sp_SP/disclaimer-sp.rst @@ -0,0 +1,6 @@ +:orphan: + +.. warning:: + Si tiene alguna duda sobre la exactitud del contenido de esta + traducción, la única referencia válida es la documentación oficial en + inglés. diff --git a/Documentation/translations/sp_SP/howto.rst b/Documentation/translations/sp_SP/howto.rst new file mode 100644 index 0000000000..f1629738b4 --- /dev/null +++ b/Documentation/translations/sp_SP/howto.rst @@ -0,0 +1,617 @@ +.. include:: ./disclaimer-sp.rst + +:Original: :ref:`Documentation/process/howto.rst ` +:Translator: Carlos Bilbao + +.. _sp_process_howto: + +Cómo participar en el desarrollo del kernel de Linux +==================================================== + +Este documento es el principal punto de partida. Contiene instrucciones +sobre cómo convertirse en desarrollador del kernel de Linux y explica cómo +trabajar con el y en su desarrollo. El documento no tratará ningún aspecto +técnico relacionado con la programación del kernel, pero le ayudará +guiándole por el camino correcto. + +Si algo en este documento quedara obsoleto, envíe parches al maintainer de +este archivo, que se encuentra en la parte superior del documento. + +Introducción +------------ +¿De modo que quiere descubrir como convertirse en un/a desarrollador/a del +kernel de Linux? Tal vez su jefe le haya dicho, "Escriba un driver de +Linux para este dispositivo." El objetivo de este documento en enseñarle +todo cuanto necesita para conseguir esto, describiendo el proceso por el +que debe pasar, y con indicaciones de como trabajar con la comunidad. +También trata de explicar las razones por las cuales la comunidad trabaja +de la forma en que lo hace. + +El kernel esta principalmente escrito en C, con algunas partes que son +dependientes de la arquitectura en ensamblador. Un buen conocimiento de C +es necesario para desarrollar en el kernel. Lenguaje ensamblador (en +cualquier arquitectura) no es necesario excepto que planee realizar +desarrollo de bajo nivel para dicha arquitectura. Aunque no es un perfecto +sustituto para una educación sólida en C y/o años de experiencia, los +siguientes libros sirven, como mínimo, como referencia: + +- "The C Programming Language" de Kernighan e Ritchie [Prentice Hall] +- "Practical C Programming" de Steve Oualline [O'Reilly] +- "C: A Reference Manual" de Harbison and Steele [Prentice Hall] + +El kernel está escrito usando GNU C y la cadena de herramientas GNU. Si +bien se adhiere al estándar ISO C89, utiliza una serie de extensiones que +no aparecen en dicho estándar. El kernel usa un C independiente de entorno, +sin depender de la biblioteca C estándar, por lo que algunas partes del +estándar C no son compatibles. Divisiones de long long arbitrarios o +de coma flotante no son permitidas. En ocasiones, puede ser difícil de +entender las suposiciones que el kernel hace respecto a la cadena de +herramientas y las extensiones que usa, y desafortunadamente no hay +referencia definitiva para estas. Consulte las páginas de información de +gcc (`info gcc`) para obtener información al respecto. + +Recuerde que está tratando de aprender a trabajar con una comunidad de +desarrollo existente. Es un grupo diverso de personas, con altos estándares +de código, estilo y procedimiento. Estas normas han sido creadas a lo +largo del tiempo en función de lo que se ha encontrado que funciona mejor +para un equipo tan grande y geográficamente disperso. Trate de aprender +tanto como le sea posible acerca de estos estándares antes de tiempo, ya +que están bien documentados; no espere que la gente se adapte a usted o a +la forma de hacer las cosas en su empresa. + +Cuestiones legales +------------------ +El código fuente del kernel de Linux se publica bajo licencia GPL. Por +favor, revise el archivo COPYING, presente en la carpeta principal del +código fuente, para detalles de la licencia. Si tiene alguna otra pregunta +sobre licencias, contacte a un abogado, no pregunte en listas de discusión +del kernel de Linux. La gente en estas listas no son abogadas, y no debe +confiar en sus opiniones en materia legal. + +Para preguntas y respuestas más frecuentes sobre la licencia GPL, consulte: + + https://www.gnu.org/licenses/gpl-faq.html + +Documentación +-------------- +El código fuente del kernel de Linux tiene una gran variedad de documentos +que son increíblemente valiosos para aprender a interactuar con la +comunidad del kernel. Cuando se agregan nuevas funciones al kernel, se +recomienda que se incluyan nuevos archivos de documentación que expliquen +cómo usar la función. Cuando un cambio en el kernel hace que la interfaz +que el kernel expone espacio de usuario cambie, se recomienda que envíe la +información o un parche en las páginas del manual que expliquen el cambio +a mtk.manpages@gmail.com, y CC la lista linux-api@vger.kernel.org. + +Esta es la lista de archivos que están en el código fuente del kernel y son +de obligada lectura: + + :ref:`Documentation/admin-guide/README.rst ` + Este archivo ofrece una breve descripción del kernel de Linux y + describe lo que es necesario hacer para configurar y compilar el + kernel. Quienes sean nuevos en el kernel deben comenzar aquí. + + :ref:`Documentation/process/changes.rst ` + Este archivo proporciona una lista de los niveles mínimos de varios + paquetes que son necesarios para construir y ejecutar el kernel + exitosamente. + + :ref:`Documentation/process/coding-style.rst ` + Esto describe el estilo de código del kernel de Linux y algunas de los + razones detrás de esto. Se espera que todo el código nuevo siga las + directrices de este documento. La mayoría de los maintainers solo + aceptarán parches si se siguen estas reglas, y muchas personas solo + revisan el código si tiene el estilo adecuado. + + :ref:`Documentation/process/submitting-patches.rst ` + Este archivo describe en gran detalle cómo crear con éxito y enviar un + parche, que incluye (pero no se limita a): + + - Contenidos del correo electrónico (email) + - Formato del email + - A quien se debe enviar + + Seguir estas reglas no garantiza el éxito (ya que todos los parches son + sujetos a escrutinio de contenido y estilo), pero en caso de no seguir + dichas reglas, el fracaso es prácticamente garantizado. + Otras excelentes descripciones de cómo crear parches correctamente son: + + "The Perfect Patch" + https://www.ozlabs.org/~akpm/stuff/tpp.txt + + "Linux kernel patch submission format" + https://web.archive.org/web/20180829112450/http://linux.yyz.us/patch-format.html + + :ref:`Documentation/process/stable-api-nonsense.rst ` + Este archivo describe la lógica detrás de la decisión consciente de + no tener una API estable dentro del kernel, incluidas cosas como: + + - Capas intermedias del subsistema (por compatibilidad?) + - Portabilidad de drivers entre sistemas operativos + - Mitigar el cambio rápido dentro del árbol de fuentes del kernel (o + prevenir cambios rápidos) + + Este documento es crucial para comprender la filosofía del desarrollo + de Linux y es muy importante para las personas que se mudan a Linux + tras desarrollar otros sistemas operativos. + + :ref:`Documentation/process/security-bugs.rst ` + Si cree que ha encontrado un problema de seguridad en el kernel de + Linux, siga los pasos de este documento para ayudar a notificar a los + desarrolladores del kernel y ayudar a resolver el problema. + + :ref:`Documentation/process/management-style.rst ` + Este documento describe cómo operan los maintainers del kernel de Linux + y los valores compartidos detrás de sus metodologías. Esta es una + lectura importante para cualquier persona nueva en el desarrollo del + kernel (o cualquier persona que simplemente sienta curiosidad por + el campo IT), ya que clarifica muchos conceptos erróneos y confusiones + comunes sobre el comportamiento único de los maintainers del kernel. + + :ref:`Documentation/process/stable-kernel-rules.rst ` + Este archivo describe las reglas sobre cómo se suceden las versiones + del kernel estable, y qué hacer si desea obtener un cambio en una de + estas publicaciones. + + :ref:`Documentation/process/kernel-docs.rst ` + Una lista de documentación externa relativa al desarrollo del kernel. + Por favor consulte esta lista si no encuentra lo que están buscando + dentro de la documentación del kernel. + + :ref:`Documentation/process/applying-patches.rst ` + Una buena introducción que describe exactamente qué es un parche y cómo + aplicarlo a las diferentes ramas de desarrollo del kernel. + +El kernel también tiene una gran cantidad de documentos que pueden ser +generados automáticamente desde el propio código fuente o desde +ReStructuredText markups (ReST), como este. Esto incluye un descripción +completa de la API en el kernel y reglas sobre cómo manejar cerrojos +(locking) correctamente. + +Todos estos documentos se pueden generar como PDF o HTML ejecutando:: + + make pdfdocs + make htmldocs + +respectivamente desde el directorio fuente principal del kernel. + +Los documentos que utilizan el markup ReST se generarán en +Documentation/output. También se pueden generar en formatos LaTeX y ePub +con:: + + make latexdocs + make epubdocs + +Convertirse en un/a desarrollador/a de kernel +--------------------------------------------- + +Si no sabe nada sobre el desarrollo del kernel de Linux, debería consultar +el proyecto Linux KernelNewbies: + + https://kernelnewbies.org + +Consiste en una útil lista de correo donde puede preguntar casi cualquier +tipo de pregunta básica de desarrollo del kernel (asegúrese de buscar en +los archivos primero, antes de preguntar algo que ya ha sido respondido en +el pasado.) También tiene un canal IRC que puede usar para hacer preguntas +en tiempo real, y una gran cantidad de documentación útil para ir +aprendiendo sobre el desarrollo del kernel de Linux. + +El sitio web tiene información básica sobre la organización del código, +subsistemas, y proyectos actuales (tanto dentro como fuera del árbol). +También describe alguna información logística básica, como cómo compilar +un kernel y aplicar un parche. + +Si no sabe por dónde quiere empezar, pero quieres buscar alguna tarea que +comenzar a hacer para unirse a la comunidad de desarrollo del kernel, +acuda al proyecto Linux Kernel Janitor: + + https://kernelnewbies.org/KernelJanitors + +Es un gran lugar para comenzar. Describe una lista de problemas +relativamente simples que deben limpiarse y corregirse dentro del código +fuente del kernel de Linux árbol de fuentes. Trabajando con los +desarrolladores a cargo de este proyecto, aprenderá los conceptos básicos +para incluir su parche en el árbol del kernel de Linux, y posiblemente +descubrir en la dirección en que trabajar a continuación, si no tiene ya +una idea. + +Antes de realizar cualquier modificación real al código del kernel de +Linux, es imperativo entender cómo funciona el código en cuestión. Para +este propósito, nada es mejor que leerlo directamente (lo más complicado +está bien comentado), tal vez incluso con la ayuda de herramientas +especializadas. Una de esas herramientas que se recomienda especialmente +es el proyecto Linux Cross-Reference, que es capaz de presentar el código +fuente en un formato de página web indexada y autorreferencial. Una +excelente puesta al día del repositorio del código del kernel se puede +encontrar en: + + https://elixir.bootlin.com/ + +El proceso de desarrollo +------------------------ + +El proceso de desarrollo del kernel de Linux consiste actualmente de +diferentes "branches" (ramas) con muchos distintos subsistemas específicos +a cada una de ellas. Las diferentes ramas son: + + - El código principal de Linus (mainline tree) + - Varios árboles estables con múltiples major numbers + - Subsistemas específicos + - linux-next, para integración y testing + +Mainline tree (Árbol principal) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +El mainline tree es mantenido por Linus Torvalds, y puede encontrarse en +https://kernel.org o en su repo. El proceso de desarrollo es el siguiente: + + - Tan pronto como se lanza un nuevo kernel, se abre una ventana de dos + semanas, durante este período de tiempo, los maintainers pueden enviar + grandes modificaciones a Linus, por lo general los parches que ya se + han incluido en el linux-next durante unas semanas. La forma preferida + de enviar grandes cambios es usando git (la herramienta de + administración de código fuente del kernel, más información al respecto + en https://git-scm.com/), pero los parches simples también son validos. + - Después de dos semanas, se lanza un kernel -rc1 y la atención se centra + en hacer el kernel nuevo lo más estable ("solido") posible. La mayoría + de los parches en este punto deben arreglar una regresión. Los errores + que siempre han existido no son regresiones, por lo tanto, solo envíe + este tipo de correcciones si son importantes. Tenga en cuenta que se + podría aceptar un controlador (o sistema de archivos) completamente + nuevo después de -rc1 porque no hay riesgo de causar regresiones con + tal cambio, siempre y cuando el cambio sea autónomo y no afecte áreas + fuera del código que se está agregando. git se puede usar para enviar + parches a Linus después de que se lance -rc1, pero los parches también + deben ser enviado a una lista de correo pública para su revisión. + - Se lanza un nuevo -rc cada vez que Linus considera que el árbol git + actual esta en un estado razonablemente sano y adecuado para la prueba. + La meta es lanzar un nuevo kernel -rc cada semana. + - El proceso continúa hasta que el kernel se considera "listo", y esto + puede durar alrededor de 6 semanas. + +Vale la pena mencionar lo que Andrew Morton escribió en las listas de +correo del kernel de Linux, sobre lanzamientos del kernel (traducido): + + *"Nadie sabe cuándo se publicara un nuevo kernel, pues esto sucede + según el estado de los bugs, no de una cronología preconcebida."* + +Varios árboles estables con múltiples major numbers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Los kernels con versiones de 3 partes son kernels estables. Estos contienen +correcciones relativamente pequeñas y críticas para problemas de seguridad +o importantes regresiones descubiertas para una publicación de código. +Cada lanzamiento en una gran serie estable incrementa la tercera parte de +la versión número, manteniendo las dos primeras partes iguales. + +Esta es la rama recomendada para los usuarios que quieren la versión +estable más reciente del kernel, y no están interesados en ayudar a probar +versiones en desarrollo/experimentales. + +Los árboles estables son mantenidos por el equipo "estable" +, y se liberan (publican) según lo dicten las +necesidades. El período de liberación normal es de aproximadamente dos +semanas, pero puede ser más largo si no hay problemas apremiantes. Un +problema relacionado con la seguridad, en cambio, puede causar un +lanzamiento casi instantáneamente. + +El archivo :ref:`Documentación/proceso/stable-kernel-rules.rst ` +en el árbol del kernel documenta qué tipos de cambios son aceptables para +el árbol estable y cómo funciona el proceso de lanzamiento. + +Subsistemas específicos +~~~~~~~~~~~~~~~~~~~~~~~~ +Los maintainers de los diversos subsistemas del kernel --- y también muchos +desarrolladores de subsistemas del kernel --- exponen su estado actual de +desarrollo en repositorios fuente. De esta manera, otros pueden ver lo que +está sucediendo en las diferentes áreas del kernel. En áreas donde el +desarrollo es rápido, se le puede pedir a un desarrollador que base sus +envíos en tal árbol del subsistema del kernel, para evitar conflictos entre +este y otros trabajos ya en curso. + +La mayoría de estos repositorios son árboles git, pero también hay otros +SCM en uso, o colas de parches que se publican como series quilt. Las +direcciones de estos repositorios de subsistemas se enumeran en el archivo +MAINTAINERS. Muchos de estos se pueden ver en https://git.kernel.org/. + +Antes de que un parche propuesto se incluya con dicho árbol de subsistemas, +es sujeto a revisión, que ocurre principalmente en las listas de correo +(ver la sección respectiva a continuación). Para varios subsistemas del +kernel, esta revisión se rastrea con la herramienta patchwork. Patchwork +ofrece una interfaz web que muestra publicaciones de parches, cualquier +comentario sobre un parche o revisiones a él, y los maintainers pueden +marcar los parches como en revisión, aceptado, o rechazado. La mayoría de +estos sitios de trabajo de parches se enumeran en + +https://patchwork.kernel.org/. + +linux-next, para integración y testing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Antes de que las actualizaciones de los árboles de subsistemas se combinen +con el árbol principal, necesitan probar su integración. Para ello, existe +un repositorio especial de pruebas en el que se encuentran casi todos los +árboles de subsistema, actualizado casi a diario: + + https://git.kernel.org/?p=linux/kernel/git/next/linux-next.git + +De esta manera, linux-next ofrece una perspectiva resumida de lo que se +espera que entre en el kernel principal en el próximo período de "merge" +(fusión de código). Los testers aventureros son bienvenidos a probar +linux-next en ejecución. + +Reportar bugs +------------- + +El archivo 'Documentación/admin-guide/reporting-issues.rst' en el +directorio principal del kernel describe cómo informar un posible bug del +kernel y detalles sobre qué tipo de información necesitan los +desarrolladores del kernel para ayudar a rastrear la fuente del problema. + +Gestión de informes de bugs +------------------------------ + +Una de las mejores formas de poner en práctica sus habilidades de hacking +es arreglando errores reportados por otras personas. No solo ayudará a +hacer el kernel más estable, también aprenderá a solucionar problemas del +mundo real y mejora sus habilidades, y otros desarrolladores se darán +cuenta de tu presencia. La corrección de errores es una de las mejores +formas de ganar méritos entre desarrolladores, porque no a muchas personas +les gusta perder el tiempo arreglando los errores de otras personas. + +Para trabajar en informes de errores ya reportados, busque un subsistema +que le interese. Verifique el archivo MAINTAINERS donde se informan los +errores de ese subsistema; con frecuencia será una lista de correo, rara +vez un rastreador de errores (bugtracker). Busque en los archivos de dicho +lugar para informes recientes y ayude donde lo crea conveniente. También es +posible que desee revisar https://bugzilla.kernel.org para informes de +errores; solo un puñado de subsistemas del kernel lo emplean activamente +para informar o rastrear; sin embargo, todos los errores para todo el kernel +se archivan allí. + +Listas de correo +----------------- + +Como se explica en algunos de los documentos anteriores, la mayoría de +desarrolladores del kernel participan en la lista de correo del kernel de +Linux. Detalles sobre cómo para suscribirse y darse de baja de la lista se +pueden encontrar en: + + http://vger.kernel.org/vger-lists.html#linux-kernel + +Existen archivos de la lista de correo en la web en muchos lugares +distintos. Utilice un motor de búsqueda para encontrar estos archivos. Por +ejemplo: + + http://dir.gmane.org/gmane.linux.kernel + +Es muy recomendable que busque en los archivos sobre el tema que desea +tratar, antes de publicarlo en la lista. Un montón de cosas ya discutidas +en detalle solo se registran en los archivos de la lista de correo. + +La mayoría de los subsistemas individuales del kernel también tienen sus +propias lista de correo donde hacen sus esfuerzos de desarrollo. Revise el +archivo MAINTAINERS para obtener referencias de lo que estas listas para +los diferentes grupos. + +Muchas de las listas están alojadas en kernel.org. La información sobre +estas puede ser encontrada en: + + http://vger.kernel.org/vger-lists.html + +Recuerde mantener buenos hábitos de comportamiento al usar las listas. +Aunque un poco cursi, la siguiente URL tiene algunas pautas simples para +interactuar con la lista (o cualquier lista): + + http://www.albion.com/netiquette/ + +Si varias personas responden a su correo, el CC (lista de destinatarios) +puede hacerse bastante grande. No elimine a nadie de la lista CC: sin una +buena razón, o no responda solo a la dirección de la lista. Acostúmbrese +a recibir correos dos veces, una del remitente y otra de la lista, y no +intente ajustar esto agregando encabezados de correo astutos, a la gente no +le gustará. + +Recuerde mantener intacto el contexto y la atribución de sus respuestas, +mantenga las líneas "El hacker John Kernel escribió ...:" en la parte +superior de su respuesta, y agregue sus declaraciones entre las secciones +individuales citadas en lugar de escribiendo en la parte superior del +correo electrónico. + +Si incluye parches en su correo, asegúrese de que sean texto legible sin +formato como se indica en :ref:`Documentation/process/submitting-patches.rst `. +Los desarrolladores del kernel no quieren lidiar con archivos adjuntos o +parches comprimidos; y pueden querer comentar líneas individuales de su +parche, que funciona sólo de esa manera. Asegúrese de emplear un programa +de correo que no altere los espacios ni los tabuladores. Una buena primera +prueba es enviarse el correo a usted mismo, e intentar aplicar su +propio parche. Si eso no funciona, arregle su programa de correo o +reemplace hasta que funcione. + +Sobretodo, recuerde de ser respetuoso con otros subscriptores. + +Colaborando con la comunidad +---------------------------- + +El objetivo de la comunidad del kernel es proporcionar el mejor kernel +posible. Cuando envíe un parche para su aceptación, se revisará en sus +méritos técnicos solamente. Entonces, ¿qué deberías ser? + + - críticas + - comentarios + - peticiones de cambios + - peticiones de justificaciones + - silencio + +Recuerde, esto es parte de introducir su parche en el kernel. Tiene que ser +capaz de recibir críticas y comentarios sobre sus parches, evaluar +a nivel técnico y re-elaborar sus parches o proporcionar razonamiento claro +y conciso de por qué no se deben hacer tales cambios. Si no hay respuestas +a su publicación, espere unos días e intente de nuevo, a veces las cosas se +pierden dado el gran volumen. + +¿Qué no debería hacer? + + - esperar que su parche se acepte sin preguntas + - actuar de forma defensiva + - ignorar comentarios + - enviar el parche de nuevo, sin haber aplicados los cambios pertinentes + +En una comunidad que busca la mejor solución técnica posible, siempre habrá +diferentes opiniones sobre lo beneficioso que es un parche. Tiene que ser +cooperativo y estar dispuesto a adaptar su idea para que encaje dentro +del kernel, o al menos esté dispuesto a demostrar que su idea vale la pena. +Recuerde, estar equivocado es aceptable siempre y cuando estés dispuesto a +trabajar hacia una solución que sea correcta. + +Es normal que las respuestas a su primer parche sean simplemente una lista +de una docena de cosas que debe corregir. Esto **no** implica que su +parche no será aceptado, y **no** es personal. Simplemente corrija todos +los problemas planteados en su parche, y envié otra vez. + +Diferencias entre la comunidad kernel y las estructuras corporativas +-------------------------------------------------------------------- + +La comunidad del kernel funciona de manera diferente a la mayoría de los +entornos de desarrollo tradicionales en empresas. Aquí hay una lista de +cosas que puede intentar hacer para evitar problemas: + + Cosas buenas que decir respecto a los cambios propuestos: + + - "Esto arregla múltiples problemas." + - "Esto elimina 2000 lineas de código." + - "Aquí hay un parche que explica lo que intento describir." + - "Lo he testeado en 5 arquitecturas distintas..." + - "Aquí hay una serie de parches menores que..." + - "Esto mejora el rendimiento en maquinas típicas..." + + Cosas negativas que debe evitar decir: + + - "Lo hicimos así en AIX/ptx/Solaris, de modo que debe ser bueno..." + - "Llevo haciendo esto 20 años, de modo que..." + - "Esto lo necesita mi empresa para ganar dinero" + - "Esto es para la linea de nuestros productos Enterprise" + - "Aquí esta el documento de 1000 paginas describiendo mi idea" + - "Llevo 6 meses trabajando en esto..." + - "Aquí esta un parche de 5000 lineas que..." + - "He rescrito todo el desastre actual, y aquí esta..." + - "Tengo un deadline, y este parche debe aplicarse ahora." + +Otra forma en que la comunidad del kernel es diferente a la mayoría de los +entornos de trabajo tradicionales en ingeniería de software, es la +naturaleza sin rostro de interacción. Una de las ventajas de utilizar el +correo electrónico y el IRC como formas principales de comunicación es la +no discriminación por motivos de género o raza. El entorno de trabajo del +kernel de Linux acepta a mujeres y minorías porque todo lo que eres es una +dirección de correo electrónico. El aspecto internacional también ayuda a +nivelar el campo de juego porque no puede adivinar el género basado en +el nombre de una persona. Un hombre puede llamarse Andrea y una mujer puede +llamarse Pat. La mayoría de las mujeres que han trabajado en el kernel de +Linux y han expresado una opinión han tenido experiencias positivas. + +La barrera del idioma puede causar problemas a algunas personas que no se +sientes cómodas con el inglés. Un buen dominio del idioma puede ser +necesario para transmitir ideas correctamente en las listas de correo, por +lo que le recomendamos que revise sus correos electrónicos para asegurarse +de que tengan sentido en inglés antes de enviarlos. + +Divida sus cambios +--------------------- + +La comunidad del kernel de Linux no acepta con gusto grandes fragmentos de +código, sobretodo a la vez. Los cambios deben introducirse correctamente, +discutidos y divididos en pequeñas porciones individuales. Esto es casi +exactamente lo contrario de lo que las empresas están acostumbradas a hacer. +Su propuesta también debe introducirse muy temprano en el proceso de +desarrollo, de modo que pueda recibir comentarios sobre lo que está +haciendo. También deje que la comunidad sienta que está trabajando con +ellos, y no simplemente usándolos como un vertedero para su función. Sin +embargo, no envíe 50 correos electrónicos a una vez a una lista de correo, +su serie de parches debe casi siempre ser más pequeña que eso. + +Las razones para dividir las cosas son las siguientes: + +1) Los cambios pequeños aumentan la probabilidad de que sus parches sean + aplicados, ya que no requieren mucho tiempo o esfuerzo para verificar su + exactitud. Un parche de 5 líneas puede ser aplicado por un maintainer + con apenas una segunda mirada. Sin embargo, un parche de 500 líneas + puede tardar horas en ser revisado en términos de corrección (el tiempo + que toma es exponencialmente proporcional al tamaño del parche, o algo + así). + + Los parches pequeños también facilitan la depuración cuando algo falla. + Es mucho más fácil retirar los parches uno por uno que diseccionar un + parche muy grande después de haber sido aplicado (y roto alguna cosa). + +2) Es importante no solo enviar pequeños parches, sino también reescribir + y simplificar (o simplemente reordenar) los parches antes de enviarlos. + +Esta es una analogía del desarrollador del kernel Al Viro (traducida): + + *"Piense en un maestro que califica la tarea de un estudiante de + matemáticas. El maestro no quiere ver los intentos y errores del + estudiante antes de que se les ocurriera la solución. Quiere ver la + respuesta más limpia y elegante. Un buen estudiante lo sabe, y nunca + presentaría su trabajo intermedio antes de tener la solución final.* + + *Lo mismo ocurre con el desarrollo del kernel. Los maintainers y + revisores no quieren ver el proceso de pensamiento detrás de la solución + al problema que se está resolviendo. Quieren ver un solución simple y + elegante."* + +Puede resultar un reto mantener el equilibrio entre presentar una solución +elegante y trabajar junto a la comunidad, discutiendo su trabajo inacabado. +Por lo tanto, es bueno comenzar temprano en el proceso para obtener +"feedback" y mejorar su trabajo, pero también mantenga sus cambios en +pequeños trozos que pueden ser aceptados, incluso cuando toda su labor no +está listo para inclusión en un momento dado. + +También tenga en cuenta que no es aceptable enviar parches para su +inclusión que están sin terminar y serán "arreglados más tarde". + +Justifique sus cambios +---------------------- + +Además de dividir sus parches, es muy importante que deje a la comunidad de +Linux sabe por qué deberían agregar este cambio. Nuevas características +debe justificarse como necesarias y útiles. + +Documente sus cambios +--------------------- + +Cuando envíe sus parches, preste especial atención a lo que dice en el +texto de su correo electrónico. Esta información se convertirá en el +ChangeLog del parche, y se conservará para que todos la vean, todo el +tiempo. Debe describir el parche por completo y contener: + + - por qué los cambios son necesarios + - el diseño general de su propuesta + - detalles de implementación + - resultados de sus experimentos + +Para obtener más detalles sobre cómo debería quedar todo esto, consulte la +sección ChangeLog del documento: + + "The Perfect Patch" + https://www.ozlabs.org/~akpm/stuff/tpp.txt + +Todas estas cuestiones son a veces son muy difíciles de conseguir. Puede +llevar años perfeccionar estas prácticas (si es que lo hace). Es un proceso +continuo de mejora que requiere mucha paciencia y determinación. Pero no se +rinda, es posible. Muchos lo han hecho antes, y cada uno tuvo que comenzar +exactamente donde está usted ahora. + +---------- + +Gracias a Paolo Ciarrocchi que permitió que la sección "Development Process" +se basara en el texto que había escrito (https://lwn.net/Articles/94386/), +y a Randy Dunlap y Gerrit Huizenga por algunas de la lista de cosas que +debes y no debes decir. También gracias a Pat Mochel, Hanna Linder, Randy +Dunlap, Kay Sievers, Vojtech Pavlik, Jan Kara, Josh Boyer, Kees Cook, +Andrew Morton, Andi Kleen, Vadim Lobanov, Jesper Juhl, Adrian Bunk, +Keri Harris, Frans Pop, David A. Wheeler, Junio Hamano, Michael Kerrisk y +Alex Shepard por su revisión, comentarios y contribuciones. Sin su ayuda, +este documento no hubiera sido posible. + +Maintainer: Greg Kroah-Hartman diff --git a/Documentation/translations/sp_SP/index.rst b/Documentation/translations/sp_SP/index.rst new file mode 100644 index 0000000000..5c2a213152 --- /dev/null +++ b/Documentation/translations/sp_SP/index.rst @@ -0,0 +1,81 @@ + +===================== +Traducción al español +===================== + +.. raw:: latex + + \kerneldocCJKoff + +:maintainer: Carlos Bilbao + +.. _sp_disclaimer: + +Advertencia +=========== + +El objetivo de esta traducción es facilitar la lectura y comprensión para +aquellos que no entiendan inglés o duden de sus interpretaciones, o +simplemente para aquellos que prefieran leer en el idioma español. Sin +embargo, tenga en cuenta que la *única* documentación oficial es la que +está en inglés: :ref:`linux_doc` + +La propagación simultánea de la traducción de una modificación en +:ref:`linux_doc` es altamente improbable. Los maintainers y colaboradores +de la traducción intentan mantener sus traducciones al día, en tanto les +es posible. Por tanto, no existe ninguna garantía de que una traducción +esté actualizada con las últimas modificaciones. Si lo que lee en una +traducción no se corresponde con lo que ve en el código fuente, informe +al maintainer de la traducción y, si puede, consulte la documentación en +inglés. + +Una traducción no es una * bifurcación * de la documentación oficial, por +lo que los usuarios no encontrarán aquí ninguna información que no sea la +versión oficial. Cualquier adición, supresión o modificación de los +contenidos deberá ser realizada anteriormente en los documentos en inglés. +Posteriormente, y cuando sea posible, dicho cambio debería aplicarse +también a las traducciones. Los maintainers de las traducciones aceptan +contribuciones que son puramente de interés relativo a la traducción (por +ejemplo, nuevas traducciones, actualizaciones, correcciones, etc.). + +Las traducciones tratan de ser lo más precisas posible pero no es posible +convertir directamente un idioma a otro. Cada idioma tiene su propia +gramática, y una cultura tras ella, por lo tanto, la traducción de una +oración al inglés se podría modificar para adaptarla al español. Por esta +razón, cuando lea esta traducción, puede encontrar algunas diferencias en +la forma, pero todavía transmiten el mensaje original. A pesar de la gran +difusión del inglés en el idioma hablado, cuando sea posible, expresiones +en inglés serán reemplazadas por las palabras correspondientes en español. + +Si necesita ayuda para comunicarse con la comunidad de Linux pero no se +siente cómodo escribiendo en inglés, puede pedir ayuda al maintainer para +obtener una traducción. + +Muchos países hablan español, cada uno con su propia cultura, expresiones, +y diferencias gramaticales en ocasiones significativas. Las traducciones de +los maintainers pueden utilizar el español con el que dichos maintainers se +sientan más cómodos. En principio, estas pequeñas diferencias no deberían +suponer una gran barrera para hablantes de distintas versiones del español, +pero en caso de duda se puede consultar a los maintainers. + +La documentación del kernel Linux +================================= + +Este es el nivel superior de la documentación del kernel en idioma español. +La traducción es incompleta, y podría encontrar advertencias que indiquen +la falta de una traducción o de un grupo de traducciones. + +En términos más generales, la documentación, como el kernel mismo, están en +constante desarrollo. Las mejoras en la documentación siempre son +bienvenidas; de modo que, si desea ayudar, únase a la lista de correo +linux-doc en vger.kernel.org. + +Traducciones al español +======================= + +.. toctree:: + :maxdepth: 1 + + howto + process/index + wrappers/memory-barriers diff --git a/Documentation/translations/sp_SP/memory-barriers.txt b/Documentation/translations/sp_SP/memory-barriers.txt new file mode 100644 index 0000000000..27097a808c --- /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. + + ====================================== + BARRERAS DE MEMORIA EN EL KERNEL LINUX + ====================================== + +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ón, la única referencia válida es la documentación oficial en +inglés. + +=========== +ADVERTENCIA +=========== + +Este documento no es una especificación; es intencionalmente (por motivos +de brevedad) y sin querer (por ser humanos) incompleta. Este documento +pretende ser una guía 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éndose al modelo de +consistencia de memoria formal y documentación en tools/memory-model/. Sin +embargo, incluso este modelo debe ser visto como la opinión colectiva de +sus maintainers en lugar de que como un oráculo infalible. + +De nuevo, este documento no es una especificación de lo que Linux espera +del hardware. + +El propósito de este documento es doble: + + (1) especificar la funcionalidad mínima en la que se puede confiar para + cualquier barrera en concreto, y + + (2) proporcionar una guía sobre cómo utilizar las barreras disponibles. + +Tenga en cuenta que una arquitectura puede proporcionar más que el +requisito mínimo para cualquier barrera en particular, pero si la +arquitectura proporciona menos de eso, dicha arquitectura es incorrecta. + +Tenga en cuenta también 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ícita resulte innecesaria en ese caso. + +========== +CONTENIDOS +========== + + (*) Modelo abstracto de acceso a memoria. + + - Operaciones del dispositivo. + - Garantías. + + (*) ¿Qué son las barreras de memoria? + + - Variedades de barrera de memoria. + - ¿Qué no se puede asumir sobre las barreras de memoria? + - Barreras de dirección-dependencia (históricas). + - Dependencias de control. + - Emparejamiento de barreras smp. + - Ejemplos de secuencias de barrera de memoria. + - Barreras de memoria de lectura frente a especulación de carga. + - Atomicidad multicopia. + + (*) Barreras explícitas del kernel. + + - Barrera del compilador. + - Barreras de memoria de la CPU. + + (*) Barreras de memoria implícitas del kernel. + + - Funciones de adquisición de cerrojo. + - Funciones de desactivación de interrupciones. + - Funciones de dormir y despertar. + - Funciones varias. + + (*) Efectos de barrera adquiriendo intra-CPU. + + - Adquisición vs accesos a memoria. + + (*) ¿Dónde se necesitan barreras de memoria? + + - Interacción entre procesadores. + - Operaciones atómicas. + - Acceso a dispositivos. + - Interrupciones. + + (*) Efectos de barrera de E/S del kernel. + + (*) Modelo de orden mínimo de ejecución asumido. + + (*) Efectos de la memoria caché de la CPU. + + - Coherencia de caché. + - Coherencia de caché frente a DMA. + - Coherencia de caché frente a MMIO. + + (*) Cosas que hacen las CPU. + + - Y luego está el Alfa. + - Guests de máquinas virtuales. + + (*) Ejemplos de usos. + + - Buffers circulares. + + (*) Referencias. + + +==================================== +MODELO ABSTRACTO DE ACCESO A MEMORIA +==================================== + +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én 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íneas discontinuas a puntos). + +Por ejemplo, considere la siguiente secuencia de eventos: + + CPU 1 CPU 2 + =============== =============== + { A == 1; B == 2 } + A = 3; x = B; + B = 4; y = 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=3, STORE B=4, y=LOAD A->3, x=LOAD B->4 +STORE A=3, STORE B=4, x=LOAD B->4, y=LOAD A->3 +STORE A=3, y=LOAD A->3, STORE B=4, x=LOAD B->4 +STORE A=3, y=LOAD A->3, x=LOAD B->2, STORE B=4 +STORE A=3, x=LOAD B->2, STORE B=4, y=LOAD A->3 +STORE A=3, x=LOAD B->2, y=LOAD A->3, STORE B=4 +STORE B=4, STORE A=3, y=LOAD A->3, x=LOAD B->4 +STORE B=4, ... +... + +y por lo tanto puede resultar en cuatro combinaciones diferentes de +valores: + +x == 2, y == 1 +x == 2, y == 3 +x == 4, y == 1 +x == 4, y == 3 + +Además, 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 + =============== =============== + { A == 1, B == 2, C == 3, P == &A, Q == &C } + B = 4; Q = P; + P = &B; D = *Q; + +Aquí hay una dependencia obvia de la dirección, ya que el valor cargado en +D depende en la dirección recuperada de P por la CPU 2. Al final de la +secuencia, cualquiera de los siguientes resultados son posibles: + + (Q == &A) y (D == 1) + (Q == &B) y (D == 2) + (Q == &B) y (D == 4) + +Tenga en cuenta que la CPU 2 nunca intentará cargar C en D porque la CPU +cargará 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és de un registro de +puerto de dirección (A) y un registro de datos del puerto (D). Para leer el +registro interno 5, el siguiente código podría entonces ser usado: + + *A = 5; + x = *D; + +pero esto podría aparecer como cualquiera de las siguientes dos secuencias: + + STORE *A = 5, x = LOAD *D + x = LOAD *D, STORE *A = 5 + +el segundo de las cuales casi con certeza resultará en mal funcionamiento, +ya que se estableció la dirección _después_ de intentar leer el registro. + + +GARANTÍAS +--------- + +Hay algunas garantías mínimas que se pueden esperar de una CPU: + + (*) En cualquier CPU dada, los accesos a la memoria dependiente se + emitirán en orden, con respeto a sí mismo. Esto significa que para: + + Q = READ_ONCE(P); D = READ_ONCE(*Q); + + donde READ_ONCE() es LEER_UNA_VEZ(), la CPU emitirá las siguientes + operaciones de memoria: + + Q = LOAD P, D = LOAD *Q + + y siempre en ese orden. Sin embargo, en DEC Alpha, READ_ONCE() también + emite una instrucción de barrera de memoria, de modo que una CPU DEC + Alpha, sin embargo emite las siguientes operaciones de memoria: + + Q = LOAD P, MEMORY_BARRIER, D = LOAD *Q, MEMORY_BARRIER + + Ya sea en DEC Alpha o no, READ_ONCE() también evita que el compilador + haga cosas inapropiadas. + + (*) Los loads y stores superpuestos dentro de una CPU en particular + parecerán ser ordenados dentro de esa CPU. Esto significa que para: + + a = READ_ONCE(*X); WRITE_ONCE(*X, b); + + donde WRITE_ONCE() es ESCRIBIR_UNA_VEZ(), la CPU solo emitirá la + siguiente secuencia de operaciones de memoria: + + a = LOAD *X, STORE *X = b + + Y para: + + WRITE_ONCE(*X, c); d = READ_ONCE(*X); + + la CPU solo emitirá: + + STORE *X = c, d = LOAD *X + + (Los loads y stores se superponen si están 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á lo que usted quiera + con referencias de memoria que no están 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ón BARRERA + DEL COMPILADOR. + + (*) _No_debe_ suponerse que se emitirán loads y stores independientes + en el orden dado. Esto significa que para: + + X = *A; Y = *B; *D = Z; + + podemos obtener cualquiera de las siguientes secuencias: + + X = LOAD *A, Y = LOAD *B, STORE *D = Z + X = LOAD *A, STORE *D = Z, Y = LOAD *B + Y = LOAD *B, X = LOAD *A, STORE *D = Z + Y = LOAD *B, STORE *D = Z, X = LOAD *A + STORE *D = Z, X = LOAD *A, Y = LOAD *B + STORE *D = Z, Y = LOAD *B, X = LOAD *A + + (*) Se _debe_ suponer que los accesos de memoria superpuestos pueden + fusionarse o ser descartados. Esto significa que para: + + X = *A; Y = *(A + 4); + + podemos obtener cualquiera de las siguientes secuencias: + +X = LOAD *A; Y = LOAD *(A + 4); +Y = LOAD *(A + 4); X = LOAD *A; +{X, Y} = LOAD {*A, *(A + 4) }; + + Y para: + +*A = X; *(A + 4) = Y; + + podemos obtener cualquiera de: + +STORE *A = X; STORE *(A + 4) = Y; +STORE *(A + 4) = Y; STORE *A = X; +STORE {*A, *(A + 4) } = {X, Y}; + +Y hay anti-garantías: + +(*) Estas garantías no se aplican a los campos de bits, porque los + compiladores a menudo generan código para modificarlos usando + secuencias de lectura-modificación-escritura no atómica. No intente + utilizar campos de bits para sincronizar algoritmos paralelos. + +(*) Incluso en los casos en que los campos de bits están protegidos por + 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án protegidos por diferentes locks, las + secuencias de lectura-modificación-escritura no atómicas del lock + pueden causar una actualización a una campo para corromper el valor de + un campo adyacente. + +(*) Estas garantías se aplican solo a escalares correctamente alineados y + dimensionados. De "tamaño adecuado" significa actualmente variables que + son del mismo tamaño que "char", "short", "int" y "long". + "Adecuadamente alineado" significa la alineación natural, por lo tanto, + no hay restricciones para "char", alineación de dos bytes para "short", + alineación de cuatro bytes para "int", y alineación de cuatro u ocho + bytes para "long", en sistemas de 32 y 64 bits, respectivamente. Tenga + en cuenta que estos garantías se introdujeron en el estándar C11, así + que tenga cuidado cuando utilice compiladores anteriores a C11 (por + ejemplo, gcc 4.6). La parte de la norma que contiene esta garantía es + la Sección 3.14, que define "ubicación de memoria" de la siguiente + manera: + + ubicación de memoria + ya sea un objeto de tipo escalar, o una secuencia máxima + de campos de bits adyacentes, todos con ancho distinto de cero + + NOTE 1: Dos hilos de ejecución 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án en ubicaciones de memoria separadas. Lo mismo sucede con + dos campos de bits, si uno se declara dentro de un declaración de + estructura anidada y el otro no, o si las dos están separados por una + declaración de campo de bits de longitud cero, o si están separados por + un miembro no declarado como campo de bits. No es seguro actualizar + simultáneamente dos campos de bits en la misma estructura si entre + todos los miembros declarados también hay campos de bits, sin importar + cuál resulta ser el tamaño de estos campos de bits intermedios. + + +================================== +¿QUÉ SON LAS BARRERAS DE MEMORIA? +================================== + +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ón 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ón 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ón de operaciones de +memoria; cargas especulativas; predicción de "branches" especulativos y +varios tipos de almacenamiento en caché. Las barreras de memoria se +utilizan para anular o suprimir estos trucos, permitiendo que el código +controle sensatamente la interacción de múltiples CPU y/o dispositivos. + + +VARIEDADES DE BARRERA DE MEMORIA +--------------------------------- + +Las barreras de memoria vienen en cuatro variedades básicas: + + (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és + 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ún efecto sobre los loads. + + Se puede considerar que una CPU envía una secuencia de operaciones de + store al sistema de memoria a medida que pasa el tiempo. Todos los + stores _antes_ de una barrera de escritura ocurrirán _antes_ de todos + los stores después 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ón); consulte la subsección + "Emparejamiento de barreras smp". + + + (2) Barrera de dependencia de dirección (histórico). + + Una barrera de dependencia de dirección es una forma más débil 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ón a la que se dirigirá el segundo + load), una barrera de dependencia de dirección sería necesaria para + asegurarse de que el objetivo de la segunda carga esté actualizado + después de acceder a la dirección obtenida por la primera carga. + + Una barrera de dependencia de direcciones es una ordenación parcial en + laods de direcciones interdependientes; no se requiere que tenga + ningún efecto en los stores, ya sean cargas de memoria o cargas + de memoria superpuestas. + + Como se mencionó en (1), las otras CPU en el sistema pueden verse como + secuencias de stores en el sistema de memoria que la considerada CPU + puede percibir. Una barrera de dependencia de dirección emitida por + la CPU en cuestión garantiza que para cualquier carga que la preceda, + 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án perceptibles por cualquier + carga emitida después la barrera de la dependencia de la dirección. + + Consulte la subsección "Ejemplos de secuencias de barrera de memoria" + para ver los diagramas mostrando las restricciones de orden. + + [!] Tenga en cuenta que la primera carga realmente tiene que tener una + dependencia de _dirección_ y no es una dependencia de control. Si la + dirección para la segunda carga depende de la primera carga, pero la + dependencia es a través de un condicional en lugar de -en realidad- + cargando la dirección en sí, entonces es una dependencia de _control_ + y se requiere una barrera de lectura completa o superior. Consulte la + subsección "Dependencias de control" para más información. + + [!] Tenga en cuenta que las barreras de dependencia de dirección + normalmente deben combinarse con barreras de escritura; consulte la + subsección "Emparejamiento de barreras smp". + + [!] Desde el kernel v5.9, se eliminó la API del kernel para barreras + de memoria de direcciones explícitas. Hoy en día, las APIs para marcar + cargas de variables compartidas, como READ_ONCE() y rcu_dereference(), + proporcionan barreras de dependencia de dirección implícitas. + + (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ás una garantía de que todas las operaciones de LOAD especificadas + antes de la barrera parecerán ocurrir antes de todas las operaciones + de LOAD especificadas después de la barrera con respecto a los demás + componentes del sistema. + + Una barrera de lectura es un orden parcial solo en cargas; no es + necesario que tenga ningún 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ón + "Emparejamiento de barreras smp". + + (4) Barreras de memoria generales + + Una barrera de memoria general proporciona la garantía de que todas + las operaciones LOAD y STORE especificadas antes de que la barrera + aparezca suceden antes de que todas las operaciones LOAD y STORE + especificadas después de la barrera con respecto a los demás + 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ícitas: + + (5) ACQUIRE (de adquisición). + + Esto actúa como una barrera permeable unidireccional. Garantiza que + toda las operaciones de memoria después de la operación ACQUIRE + parezcan suceder después de la ACQUIRE con respecto a los demás + 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ón ACQUIRE + pueden parecer suceder después de que se complete. + + Una operación ACQUIRE casi siempre debe estar emparejada con una + operación RELEASE (de liberación). + + + (6) Operaciones RELEASE (de liberación). + + Esto también actúa como una barrera permeable unidireccional. + Garantiza que todas las operaciones de memoria antes de la operación + RELEASE parecerán ocurrir antes de la operación RELEASE con respecto a + los demás componentes del sistema. Las operaciones de RELEASE incluyen + operaciones de UNLOCK y operaciones smp_store_release(). + + Las operaciones de memoria que ocurren después de una operación + 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ás, un par + RELEASE+ACQUIRE NO garantiza actuar como una barrera de memoria + completa. Sin embargo, después de un ACQUIRE de una variable dada, + todos los accesos a la memoria que preceden a cualquier anterior + RELEASE en esa misma variable están garantizados como visibles. En + otras palabras, dentro de la sección crítica de una variable dada, + todos los accesos de todas las secciones críticas anteriores para esa + variable habrán terminado de forma garantizada. + + Esto significa que ACQUIRE actúa como una operación mínima de + "adquisición" y RELEASE actúa como una operación mínima de + "liberación". + +Un subconjunto de las operaciones atómicas descritas en atomic_t.txt +contiene variantes de ACQUIRE y RELEASE, además de definiciones +completamente ordenadas o relajadas (sin barrera semántica). Para +composiciones atómicas que realizan tanto un load como store, la semántica +ACQUIRE se aplica solo a la carga y la semántica RELEASE se aplica sólo a +la parte de la operación del store. + +Las barreras de memoria solo son necesarias cuando existe la posibilidad de +interacción entre dos CPU o entre una CPU y un dispositivo. Si se puede +garantizar que no habrá tal interacción en ninguna pieza de código en +particular, entonces las barreras de memoria son innecesarias en ese +fragmento de código. + +Tenga en cuenta que estas son las garantías _mínimas_. Diferentes +arquitecturas pueden proporcionar garantías más sustanciales, pero no se +puede confiar en estas fuera de esa arquitectura en específico. + + +¿QUÉ 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ía de que ninguno de los accesos a la memoria + especificados antes de una barrera de memoria estará _completo_ al + completarse una instrucción de barrera de memoria; se puede considerar + que la barrera dibuja una línea en la cola de acceso del CPU que no + pueden cruzar los accesos del tipo correspondiente. + + (*) No hay garantía de que la emisión 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á el orden en que la segunda CPU + ve los efectos de los primeros accesos que ocurren de la CPU, pero lea + el siguiente argumento: + + (*) No hay garantía de que una CPU vea el orden correcto de los efectos + de los accesos de una segunda CPU, incluso _si_ la segunda CPU usa una + barrera de memoria, a menos que la primera CPU _también_ use una + barrera de memoria coincidente (vea el subapartado "Emparejamiento de + barrera SMP"). + + (*) No hay garantía de que alguna pieza intermedia fuera del hardware[*] + del CPU no reordenará los accesos a la memoria. Los mecanismos de + coherencia de caché del CPU deben propagar los efectos indirectos de + una barrera de memoria entre las CPU, pero es posible que no lo hagan + en orden. + + [*] Para obtener información sobre bus mastering DMA y coherencia, lea: + + Documentation/driver-api/pci/pci.rst + Documentation/core-api/dma-api-howto.rst + Documentation/core-api/dma-api.rst + + +BARRERA DE DEPENDENCIA DE DIRECCIÓN (HISTÓRICO) +----------------------------------------------- + +A partir de la versión 4.15 del kernel Linux, se agregó un smp_mb() a +READ_ONCE() para DEC Alpha, lo que significa que las únicas personas que +necesitan prestar atención a esta sección son aquellas que trabajan en el +código específico de la arquitectura DEC Alpha y aquellas que trabajan en +READ_ONCE() por dentro. Para aquellos que lo necesitan, y para aquellos que +estén interesados desde un punto de vista histórico, aquí está la historia +de las barreras de dependencia de dirección. + +[!] 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ón no son necesarias para situaciones de carga a store. + +El requisito de las barreras de dependencia de dirección es un poco sutil, +y no siempre es obvio que sean necesarias. Para ilustrar, considere la +siguiente secuencia de eventos: + + CPU 1 CPU 2 + =============== =============== + { A == 1, B == 2, C == 3, P == &A, Q == &C } + B = 4; + + WRITE_ONCE(P, &B); + Q = READ_ONCE_OLD(P); + D = *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ón aquí, y parecería que al final de +la secuencia, Q debe ser &A o &B, y que: + + (Q == &A) implica (D == 1) + (Q == &B) implica (D == 4) + +¡Pero! La percepción de la CPU 2 de P puede actualizarse _antes_ de su +percepción de B, por lo tanto dando lugar a la siguiente situación: + + (Q == &B) y (D == 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ón implícita desde el lanzamiento del kernel v4.15: + + CPU 1 CPU 2 + =============== =============== + { A == 1, B == 2, C == 3, P == &A, Q == &C } + B = 4; + + WRITE_ONCE(P, &B); + Q = READ_ONCE(P); + + D = *Q; + +Esto refuerza la ocurrencia de una de las dos implicaciones, y previene la +tercera posibilidad de surgir. + + +[!] Tenga en cuenta que esta situación extremadamente contraria a la +intuición surge más fácilmente en máquinas con cachés divididos, de modo +que, por ejemplo, un banco de caché procesa líneas de caché pares y el otro +banco procesa líneas impares de caché. El puntero P podría almacenarse en +una línea de caché impar y la variable B podría almacenarse en una línea de +caché con número par. Entonces, si el banco de números pares de la memoria +caché de la CPU de lectura está extremadamente ocupado mientras que el +banco impar está inactivo, uno podría ver el nuevo valor del puntero P +(&B), pero el antiguo valor de la variable B (2). + + +No se requiere una barrera de dependencia de dirección para ordenar +escrituras dependientes porque las CPU que admite el kernel Linux no +escriben hasta que están seguros (1) de que la escritura realmente +sucederá, (2) de la ubicación de la escritura, y (3) del valor a escribir. +Pero, por favor, lea atentamente la sección "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 + =============== =============== + { A == 1, B == 2, C = 3, P == &A, Q == &C } + B = 4; + + WRITE_ONCE(P, &B); + Q = 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á prohibido, incluso sin una barrera de dependencia de +dirección implícita del READ_ONCE() moderno: + + (Q == &B) && (B == 4) + +Tenga en cuenta que este patrón debe ser raro. Después de todo, el objetivo +del orden de dependencia es -prevenir- escrituras en la estructura de +datos, junto con los costosos errores de caché asociados con tales +escrituras. Este patrón se puede utilizar para registrar raras condiciones +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ón +es local para la CPU que lo contiene. Lea la sección sobre "Atomicidad +multicopia" para más información. + + +La barrera de dependencia de dirección es muy importante para el sistema +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én la subsección sobre "Coherencia de caché" para obtener un +ejemplo más completo. + +DEPENDENCIAS DE CONTROL +----------------------- + +Las dependencias de control pueden ser un poco complicadas porque los +compiladores actuales no las entienden. El propósito de esta sección es +ayudarle a prevenir que la ignorancia del compilador rompa su código. + +Una dependencia de control load-load (de carga a carga) requiere una +barrera de memoria de lectura completa, no simplemente una barrera +(implícita) de dependencia de direcciones para que funcione correctamente. +Considere el siguiente fragmento de código: + + q = READ_ONCE(a); + + if (q) { + /* BUG: No hay dependencia de dirección!!! */ + p = READ_ONCE(b); + } + +Esto no tendrá el efecto deseado porque no hay una dependencia de dirección +real, sino más 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 = READ_ONCE(a); + if (q) { + + p = 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 = 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ía combinar la carga de +'a' con otras cargas de 'a'. Sin WRITE_ONCE(), el compilador podría +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ón. + +Peor aún, si el compilador puede probar (decir) que el valor de la +variable 'a' siempre es distinta de cero, estaría dentro de sus derechos +para optimizar el ejemplo original eliminando la declaración "if", como: + + q = a; + b = 1; /* BUG: Compilador y CPU pueden ambos reordernar!!! */ + +Así que no deje de lado READ_ONCE(). + +Es tentador tratar de hacer cumplir el orden en stores idénticos en ambos +caminos del "if" de la siguiente manera: + + q = 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án esto de la +siguiente manera en casos de alto nivel de optimización: + + q = 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á en su derecho de reordenarlos: El condicional es +absolutamente necesario y debe estar presente en el código ensamblador +incluso después de que se hayan aplicado todas las optimizaciones del +compilador. Por lo tanto, si necesita ordenar en este ejemplo, necesita +explícitamente barreras de memoria, por ejemplo, smp_store_release(): + + + q = 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ícita, el control de un if +con dos opciones está garantizado solo cuando los stores difieren, por +ejemplo: + + q = READ_ONCE(a); + if (q) { + WRITE_ONCE(b, 1); + hacer_algo(); + } else { + WRITE_ONCE(b, 2); + hacer_otra_cosa(); + } + +Aún se requiere el inicial READ_ONCE() para evitar que el compilador toque +el valor de 'a'. + +Además, debe tener cuidado con lo que hace con la variable local 'q', de lo +contrario, el compilador podría adivinar el valor y volver a eliminar el +necesario condicional. Por ejemplo: + + q = 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ódigo +anterior en el siguiente: + + q = READ_ONCE(a); + WRITE_ONCE(b, 2); + hacer_otra_cosa(); + +Dada esta transformación, la CPU no está 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á 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 = READ_ONCE(a); + BUILD_BUG_ON(MAX <= 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ás que los stores de 'b' difieren. Si fueran +idénticos, como se señaló anteriormente, el compilador podría sacar ese +store fuera de la declaración 'if'. + +También debe tener cuidado de no confiar demasiado en el cortocircuito +de la evaluación booleana. Considere este ejemplo: + + q = READ_ONCE(a); + if (q || 1 > 0) + WRITE_ONCE(b, 1); + +Debido a que la primera condición no puede fallar y la segunda condición es +siempre cierta, el compilador puede transformar este ejemplo de la +siguiente manera, rompiendo la dependencia del control: + + q = READ_ONCE(a); + WRITE_ONCE(b, 1); + +Este ejemplo subraya la necesidad de asegurarse de que el compilador no +pueda adivinar su código. Más generalmente, aunque READ_ONCE() fuerza +al compilador para emitir código para una carga dada, no fuerza al +compilador para usar los resultados. + +Además, las dependencias de control se aplican solo a la cláusula then y +la cláusula else de la sentencia if en cuestión. En particular, no se +aplica necesariamente al código que sigue a la declaración if: + + q = 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átiles y tampoco puede reordenar escrituras +en 'b' con la condición. Desafortunadamente para esta línea de +razonamiento, el compilador podría compilar las dos escrituras en 'b' como +instrucciones de movimiento condicional, como en este fantástico idioma +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ébilmente ordenada no tendría dependencia de ningún tipo entre la +carga de 'a' y el store de 'c'. Las dependencias de control se extenderían +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áusula then y la cláusula else de la sentencia if en cuestión (incluidas +las funciones invocado por esas dos cláusulas), no al código que sigue a +esa declaración 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ás información. + + +En resumen: + + (*) Las dependencias de control pueden ordenar cargas anteriores para + stores posteriores. Sin embargo, no garantizan ningún 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ón "if" comienzan con stores + idénticos de la misma variable, entonces esos stores deben ser + ordenados, ya sea precediéndoles 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ón "if" porque, como se muestra en el ejemplo anterior, la + optimización 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ón 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én + habrá 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ón BARRERA DEL COMPILADOR + para obtener más información al respecto. + + (*) Las dependencias de control se aplican solo a la cláusula then y la + cláusula else de la sentencia "if" que contiene la dependencia de + control, incluyendo cualquier función a la que llamen dichas dos + cláusulas. Las dependencias de control no se aplican al código que + sigue a la instrucción if que contiene la dependencia de control. + + (*) 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ódigo. + + +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í, aunque también se emparejan +con la mayoría de otro tipo de barreras, aunque sin atomicidad multicopia. +Una barrera de adquisición se empareja con una barrera de liberación, pero +ambas también pueden emparejarse con otras barreras, incluidas, por +supuesto, las barreras generales. Una barrera de escritura se empareja con +una barrera de dependencia de dirección, una dependencia de control, una +barrera de adquisición, una barrera de liberación, una barrera de 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ón con +una barrera de escritura, una barrera de adquisición, una barrera de +liberación o una barrera general: + + CPU 1 CPU 2 + =============== =============== + WRITE_ONCE(a, 1); + + WRITE_ONCE(b, 2); x = READ_ONCE(b); + + y = READ_ONCE(a); + +O bien: + + CPU 1 CPU 2 + =============== =============================== + a = 1; + + WRITE_ONCE(b, &a); x = READ_ONCE(b); + + y = *x; + +O incluso: + + CPU 1 CPU 2 + =============== =============================== + r1 = READ_ONCE(y); + + WRITE_ONCE(x, 1); if (r2 = READ_ONCE(x)) { + + WRITE_ONCE(y, 1); + } + + assert(r1 == 0 || r2 == 0); + +Básicamente, la barrera de lectura siempre tiene que estar ahí, aunque +puede ser del tipo "más débil". + +[!] Tenga en cuenta que normalmente se esperaría que los stores antes de la +barrera de escritura se hagan coincidir con los stores después de la +barrera de lectura o la barrera de dependencia de dirección, y viceversa: + + CPU 1 CPU 2 + =================== =================== + WRITE_ONCE(a, 1); }---- --->{ v = READ_ONCE(c); + WRITE_ONCE(b, 2); } \ / { w = READ_ONCE(d); + \ + WRITE_ONCE(c, 3); } / \ { x = READ_ONCE(a); + WRITE_ONCE(d, 4); }---- --->{ y = READ_ONCE(b); + + +EJEMPLOS DE SECUENCIAS DE BARRERA DE MEMORIA +-------------------------------------------- + +En primer lugar, las barreras de escritura actúan como orden parcial en las +operaciones de store. Considere la siguiente secuencia de eventos: + + CPU 1 + ======================= + STORE A = 1 + STORE B = 2 + STORE C = 3 + + STORE D = 4 + STORE E = 5 + +Esta secuencia de eventos es finalizado para con el sistema de coherencia +de memoria en un orden que el resto del sistema podría percibir como el +conjunto desordenado { STORE A, STORE B, STORE C} todo ocurriendo antes del +conjunto desordenado { STORE D, STORE E}: + + + +-------+ : : + | | +------+ + | |------>| C=3 | } /\ + | | : +------+ }----- \ -----> Eventos perceptibles para + | | : | A=1 | } \/ el resto del sistema + | | : +------+ } + | CPU 1 | : | B=2 | } + | | +------+ } + | | wwwwwwwwwwwwwwww } <--- En este momento la barrera de + | | +------+ } escritura requiere que todos los + | | : | E=5 | } stores anteriores a la barrera + | | : +------+ } sean confirmados antes de que otros + | |------>| D=4 | } 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ón actúan como +órdenes parciales sobre la dirección de cargas dependientes. Considere la +siguiente secuencia de eventos: + + CPU 1 CPU 2 + ======================= ======================= + { B = 7; X = 9; Y = 8; C = &Y } + STORE A = 1 + STORE B = 2 + + STORE C = &B LOAD X + STORE D = 4 LOAD C (consigue &B) + LOAD *C (lee B) + +Sin intervención, la CPU 2 puede percibir los eventos en la CPU 1 en orden +aleatorio a efectos prácticos, a pesar de la barrera de escritura emitida +por la CPU 1: + + +-------+ : : : : + | | +------+ +-------+ | Secuencia de + | |------>| B=2 |----- --->| Y->8 | | actualizado de + | | : +------+ \ +-------+ | percepción en CPU 2 + | CPU 1 | : | A=1 | \ --->| C->&Y | V + | | +------+ | +-------+ + | | wwwwwwwwwwwwwwww | : : + | | +------+ | : : + | | : | C=&B |--- | : : +-------+ + | | : +------+ \ | +-------+ | | + | |------>| D=4 | ----------->| C->&B |------>| | + | | +------+ | +-------+ | | + +-------+ : : | : : | | + | : : | | + | : : | CPU 2 | + | +-------+ | | + Percepción 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ía B) viniendo después del LOAD de C. + +Sin embargo, si se colocara una barrera de dependencia de dirección entre +la carga de C y la carga de *C (es decir: B) en la CPU 2: + + CPU 1 CPU 2 + ======================= ======================= + { B = 7; X = 9; Y = 8; C = &Y } + STORE A = 1 + STORE B = 2 + + STORE C = &B LOAD X + STORE D = 4 LOAD C (consigue &B) + + LOAD *C (reads B) + +entonces ocurrirá lo siguiente: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| B=2 |----- --->| Y->8 | + | | : +------+ \ +-------+ + | CPU 1 | : | A=1 | \ --->| C->&Y | + | | +------+ | +-------+ + | | wwwwwwwwwwwwwwww | : : + | | +------+ | : : + | | : | C=&B |--- | : : +-------+ + | | : +------+ \ | +-------+ | | + | |------>| D=4 | ----------->| 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úa como un orden parcial sobre +las cargas. Considere la siguiente secuencia de eventos: + + CPU 1 CPU 2 + ======================= ======================= + { A = 0, B = 9 } + STORE A=1 + + STORE B=2 + LOAD B + LOAD A + +Sin intervención, la CPU 2 puede elegir percibir los eventos en la CPU 1 en +algún orden aleatorio a efectos prácticos, a pesar de la barrera de +escritura emitida por la CPU 1: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| A=1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| 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 + ======================= ======================= + { A = 0, B = 9 } + STORE A=1 + + STORE B=2 + LOAD B + + LOAD A + +entonces el orden parcial impuesto por la CPU 1 será percibido +correctamente por la CPU 2: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| A=1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| 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ás completa, considere lo que podría pasar si +el código contenía una carga de A a cada lado de la barrera de lectura: + + CPU 1 CPU 2 + ======================= ======================= + { A = 0, B = 9 } + STORE A=1 + + STORE B=2 + LOAD B + LOAD A [primer load de A] + + LOAD A [segundo load de A] + +Aunque las dos cargas de A ocurren después de la carga de B, ambas pueden +obtener diferentes valores: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| A=1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| 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ón 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=1 |------ --->| A->0 | + | | +------+ \ +-------+ + | CPU 1 | wwwwwwwwwwwwwwww \ --->| B->9 | + | | +------+ | +-------+ + | |------>| B=2 |--- | : : + | | +------+ \ | : : +-------+ + +-------+ : : \ | +-------+ | | + ---------->| B->2 |------>| | + | +-------+ | CPU 2 | + | : : | | + \ : : | | + \ +-------+ | | + ---->| A->1 |------>| 1st | + +-------+ | | + rrrrrrrrrrrrrrrrr | | + +-------+ | | + | A->1 |------>| 2nd | + +-------+ | | + : : +-------+ + +La garantía es que la segunda carga siempre dará como resultado A == 1 si +la carga de B resultó en B == 2. No existe tal garantía para la primera +carga de A; esto puede dar como resultado A == 0 o A == 1. + + +BARRERAS DE MEMORIA DE LECTURA FRENTE A ESPECULACIÓN DE CARGA +------------------------------------------------------------- + +Muchas CPU especulan con las cargas: es decir, ven que necesitarán cargar +un elemento de la memoria, y encuentran un momento en el que no están +usando el bus para ningún otra carga, y también en la carga por adelantado, +aunque en realidad no lo hayan llegado a ese punto en el flujo de ejecución +de instrucciones todavía. Esto permite que la instrucción de carga 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ón eludió la carga, en cuyo caso puede descartar el valor +o simplemente almacenar en caché para su uso posterior. + +Considere: + + CPU 1 CPU 2 + ======================= ======================= + LOAD B + DIVIDE } Instrucciones de división + DIVIDE } tardan mucho en terminar + LOAD A + +donde DIVIDE es DIVIDIR. Que podría aparecer como esto: + + : : +-------+ + +-------+ | | + --->| B->2 |------>| | + +-------+ | CPU 2 | + : :DIVIDE | | + +-------+ | | + La CPU ocupada con la división ---> --->| 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ón +justo antes de la segundo carga: + + + + CPU 1 CPU 2 + ======================= ======================= + LOAD B + DIVIDE + DIVIDE + + LOAD A + +obligará a reconsiderar cualquier valor obtenido especulativamente en una +medida dependiente del tipo de barrera utilizada. Si no se hizo ningún +cambio en la ubicación de memoria especulada, entonces el valor especulado +solo se usará: + + : : +-------+ + +-------+ | | + --->| B->2 |------>| | + +-------+ | CPU 2 | + : :DIVIDE | | + +-------+ | | + La CPU ocupada con la división ---> --->| A->0 |~~~~ | | + especula sobre el LOAD de A +-------+ ~ | | + : : ~ | | + : :DIVIDE | | + : : ~ | | + : : ~ | | + rrrrrrrrrrrrrrrr~ | | + : : ~ | | + : : ~-->| | + : : | | + : : +-------+ + + +pero si había una actualización o una invalidación de otra CPU pendiente, +entonces la especulación será cancelada y el valor recargado: + + : : +-------+ + +-------+ | | + --->| B->2 |------>| | + +-------+ | CPU 2 | + : :DIVIDE | | + +-------+ | | + La CPU ocupada con la división ---> --->| A->0 |~~~~ | | + especula sobre el LOAD de A +-------+ ~ | | + : : ~ | | + : :DIVIDE | | + : : ~ | | + : : ~ | | + rrrrrrrrrrrrrrrrr | | + +-------+ | | + La especulación es descartada ---> --->| A->1 |------>| | + y un valor actualizado +-------+ | | + es conseguido : : +-------+ + +ATOMICIDAD MULTICOPIA +--------------------- + +La atomicidad multicopia es una noción profundamente intuitiva sobre el +orden que no es siempre proporcionada por los sistemas informáticos reales, +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ía valiosas optimizaciones +hardware, por lo que una versión más débil 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ón más débil, pero por brevedad lo llamaremos simplemente +``atomicidad multicopia''. + +El siguiente ejemplo demuestra la atomicidad multicopia: + + CPU 1 CPU 2 CPU 3 + ======================= ======================= ======================= + { X = 0, Y = 0 } + STORE X=1 r1=LOAD X (reads 1) LOAD Y (reads 1) + + STORE Y=r1 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ás, las barreras de memoria +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 +"¿Puede 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és +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ó originalmente +el valor que leyó), entonces en sistemas atómicos multicopia, la carga de +la CPU B debe devolver el mismo valor que hizo la carga de la CPU A o algún +valor posterior. Sin embargo, el kernel Linux no requiere que los sistemas +sean atómicos 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én 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ón: + + CPU 1 CPU 2 CPU 3 + ======================= ======================= ======================= + { X = 0, Y = 0 } + STORE X=1 r1=LOAD X (escribe 1) LOAD Y (lee 1) + + STORE Y=r1 LOAD X (lee 0) + +Esta sustitución permite que la atomicidad no multicopia se desenfrene: 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ómico no multicopia donde las CPU 1 +y 2 comparten un buffer de almacenamiento o un nivel de caché, la CPU 2 +podría 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últiples. + +Las barreras generales pueden compensar no solo la atomicidad no +multicopia, pero también pueden generar orden adicional que puede asegurar +que -todas- las CPU percibirán el mismo orden de -todas- las operaciones. +Por el contrario, una cadena de parejas de liberación-adquisición no +proporciona este orden adicional, lo que significa que solo se garantiza +que las CPU de la cadena estén de acuerdo en el orden combinado de los +accesos. Por ejemplo, cambiando a código C en deferencia al fantasma de +Herman Hollerith: + + int u, v, x, y, z; + + void cpu0(void) + { + r0 = smp_load_acquire(&x); + WRITE_ONCE(u, 1); + smp_store_release(&y, 1); + } + + void cpu1(void) + { + r1 = smp_load_acquire(&y); + r4 = READ_ONCE(v); + r5 = READ_ONCE(u); + smp_store_release(&z, 1); + } + + void cpu2(void) + { + r2 = smp_load_acquire(&z); + smp_store_release(&x, 1); + } + + void cpu3(void) + { + WRITE_ONCE(v, 1); + smp_mb(); + r3 = 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ía +prohibido: + + r0 == 1 && r1 == 1 && r2 == 1 + +Además, debido a la relación liberación-adquisición entre cpu0() y cpu1(), +cpu1() debe ver las escrituras de cpu0(), de modo que el siguiente +resultado estaría prohibido: + + r1 == 1 && r5 == 0 + +Sin embargo, el orden proporcionado por una cadena de +liberación-adquisición 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 == 0 && r1 == 1 && r2 == 1 && r3 == 0 && r4 == 0 + +Por otro lado, también el siguiente resultado es posible: + + r0 == 0 && r1 == 1 && r2 == 1 && r3 == 0 && r4 == 0 && r5 == 1 + +Aunque cpu0(), cpu1() y cpu2() verán sus respectivas lecturas y escrituras +en orden, las CPU que no participan en la cadena de liberación-adquisición +pueden estar en desacuerdo con el orden. Este desacuerdo se debe al hecho +de que las instrucciones de barrera de memoria débiles 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és- de +la carga de cpu1() desde v, aunque tanto cpu0() como cpu1() están de +acuerdo en que estas dos operaciones ocurrieron en el orden previsto. + +Sin embargo, tenga en cuenta que smp_load_acquire() no es mágico. En +particular, simplemente lee de su argumento en orden. Es decir, -no- +asegura que se leerá cualquier valor en particular. Por lo tanto, los +siguiente resultados son posibles: + + r0 == 0 && r1 == 0 && r2 == 0 && r5 == 0 + +Tenga en cuenta que este resultado puede ocurrir incluso en un mítico +sistema, consistente en secuencia, donde nunca se reordena nada. + +Para reiterar, si su código requiere un orden completo de todas las +operaciones, utilice barreras generales en todo momento. + + +============================== +BARRERAS EXPLÍCITAS DEL KERNEL +============================== + +El kernel Linux tiene una variedad de diferentes barreras que actúan a +diferentes niveles: + + (*) Barrera del compilador. + + (*) Barreras de memoria de la CPU. + + +BARRERA DEL COMPILADOR +----------------------- + +El kernel de Linux tiene una función de barrera del compilador explícita +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ébiles de barrier() que +afectan solo específicos accesos marcados por READ_ONCE() o WRITE_ONCE(). + +La función 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ón entre código del + interrupt-handler (encargo de gestionar interrupciones) y el código + 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és de + ese loop. + +Las funciones READ_ONCE() y WRITE_ONCE() pueden evitar cualquier cantidad +de optimizaciones que, si bien son perfectamente seguras en código de un +solo subproceso, pueden resultar fatales en código concurrente. Aquí hay +algunos ejemplos de tal tipo de optimizaciones: + +(*) El compilador está en su derecho de reordenar cargas y stores de la + misma variable, y en algunos casos, la CPU está dentro de su + derecho de reordenar cargas a la misma variable. Esto significa que + el siguiente código: + + a[0] = x; + a[1] = x; + + Podría resultar en un valor más 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] = READ_ONCE(x); + a[1] = READ_ONCE(x); + + En resumen, READ_ONCE() y WRITE_ONCE() proporcionan coherencia de + caché para accesos desde múltiples CPUs a una sola variable. + + (*) El compilador tiene derecho a juntar cargas sucesivas de la misma + variable. Tal fusión puede hacer que el compilador "optimice" el + siguiente código: + + while (tmp = a) + hacer_algo_con(tmp); + + en el siguiente código, que, aunque en cierto sentido es legítimo + para un código de un solo subproceso, es casi seguro que no es lo + que el desarrollador pretendía: + + if (tmp = a) + for (;;) + hacer_algo_con(tmp); + + Use READ_ONCE() para evitar que el compilador le haga esto: + + while (tmp = 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ón de los registros impida que el + compilador mantenga todos los datos de interés en registros. El + compilador podría por lo tanto, optimizar la variable 'tmp' de nuestro + ejemplo anterior: + + while (tmp = a) + hacer_algo_con(tmp); + + Esto podría resultar en el siguiente código, que es perfectamente + seguro en código de subproceso único, pero puede ser fatal en código + concurrente: + + while (a) + hacer_algo_con(a); + + Por ejemplo, la versión optimizada de este código podrí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ón "while" y la + llamada a hacer_algo_con(). + + De nuevo, use READ_ONCE() para evitar que el compilador haga esto: + + while (tmp = READ_ONCE(a)) + hacer_algo_con(tmp); + + Tenga en cuenta que si el compilador se queda sin registros, podría + guardar tmp en la pila ("stack"). El overhead (coste en eficiencia) de + este guardado y posterior restauración es por lo que los compiladores + recargan las variables. Hacerlo es perfectamente seguro para código de + subproceso único, por lo que debe informar al compilador sobre los + casos donde no sea seguro. + + (*) El compilador está en su derecho de omitir una carga por completo si + sabe cual será su valor. Por ejemplo, si el compilador puede probar + que el valor de la variable 'a' siempre es cero, puede optimizar este + código: + + while (tmp = a) + hacer_algo_con(tmp); + + En esto: + + do { } while (0); + + Esta transformación es una victoria para un código de un solo + subproceso, porque se deshace de una carga y un branch. El problema es + que el compilador llevará a cabo su prueba asumiendo que la CPU actual + es la única actualizando la variable 'a'. Si la variable 'a' es + compartida, entonces la prueba del compilador será errónea. Use + READ_ONCE() para decirle al compilador que no sabe tanto como cree: + + while (tmp = READ_ONCE(a)) + hacer_algo_con(tmp); + + Pero, por favor, tenga en cuenta que el compilador también está + observando de cerca lo que usted hace con el valor después de + READ_ONCE(). Por ejemplo, suponga que Ud. hace lo siguiente y MAX es + una macro de preprocesador con el valor 1: + + while ((tmp = READ_ONCE(a)) % MAX) + hacer_algo_con(tmp); + + Entonces el compilador sabe que el resultado del operador "%" aplicado + a MAX siempre será cero, nuevamente permitiendo que el compilador + optimice el código hasta su casi inexistencia. (Aún se cargará 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 única 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 = 0; + ... Código que no almacena la variable a ... + a = 0; + + El compilador observa que el valor de la variable 'a' ya es cero, por + lo que bien podría omitir el segundo store. Esto supondría 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ón equivocada: + + WRITE_ONCE(a, 0); + ... Código 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ón + entre el código de nivel de proceso y un controlador de interrupción: + + void nivel_de_procesamiento(void) + { + msg = ACQUIRE_mensaje(); + flag = 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ía ser + una victoria para código de un solo subproceso: + + void nivel_de_procesamiento(void) + { + flag = true; + msg = ACQUIRE_mensaje(); + } + + Si la interrupción ocurre entre estas dos declaraciones, entonces + controlador_interrupcion() podría recibir un mensaje ilegible. Use + 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én accede a 'flag' y 'msg', por ejemplo, una interrupción anidada + o un NMI. De lo contrario, READ_ONCE() y WRITE_ONCE() no son + necesarios en controlador_interrupcion() aparte de con fines de + documentación. (Tenga también en cuenta que las interrupciones + anidadas no ocurren típicamente en los kernels Linux modernos, de + hecho, si un controlador de interrupciones regresa con interrupciones + habilitadas, obtendrá un WARN_ONCE().) + + Debe suponer que el compilador puede mover READ_ONCE() y WRITE_ONCE() + a código que no contiene READ_ONCE(), WRITE_ONCE(), barrier(), o + primitivas similares. + + Este efecto también podría lograrse usando barrier(), pero READ_ONCE() + y WRITE_ONCE() son más 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é, en cualquier registro de la máquina. + Por supuesto, el compilador también 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 = a; + else + b = 42; + + El compilador podría ahorrar un branch al optimizar esto de la + siguiente manera: + + b = 42; + if (a) + b = a; + + En el código de un solo subproceso, esto no solo es seguro, sino que + también ahorra un branch. Desafortunadamente, en código concurrente, + esta optimización podría 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én puede inventar cargas. Estos casos suelen ser + menos perjudiciales, pero pueden dar como resultado "bouncing" de la + línea de caché y, por lo tanto, bajo rendimiento y escalabilidad. + Utilice READ_ONCE() para evitar cargas inventadas. + + (*) Para ubicaciones de memoria alineadas cuyo tamaño les permita + acceder con una sola instrucción 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últiples + accesos menores. Por ejemplo, dada una arquitectura que tiene + instrucciones de almacenamiento de 16 bits con campos inmediatos de 7 + bits, el compilador podría tener la tentación de usar dos + instrucciones inmediatas de almacenamiento de 16 bits para implementar + el siguiente store de 32 bits: + + p = 0x00010002; + + Tenga en cuenta que GCC realmente usa este tipo de optimización, lo + cual no es sorprendente dado que probablemente costaría más de dos + instrucciones el construir la constante y luego almacenarla. Por lo + tanto, esta optimización puede ser una victoria en un código de un + solo subproceso. De hecho, un error reciente (desde que se solucionó) + hizo que GCC usara incorrectamente esta optimización en un store + volátil. En ausencia de tales errores, el uso de WRITE_ONCE() evita el + desgarro del store en el siguiente ejemplo: + + struct __attribute__((__packed__)) foo { + short a; + int b; + short c; + }; + struct foo foo1, foo2; + ... + + foo2.a = foo1.a; + foo2.b = foo1.b; + foo2.c = foo1.c; + + Debido a que no hay envoltorios READ_ONCE() o WRITE_ONCE() y no + hay markings volátiles, el compilador estaría en su derecho de + implementar estas tres declaraciones de asignación como un par de + cargas de 32 bits, seguido de un par de stores de 32 bits. Esto + resultaría en una carga con desgarro en 'foo1.b' y store del desgarro + en 'foo2.b'. READ_ONCE() y WRITE_ONCE() nuevamente evitan el desgarro + en este ejemplo: + + foo2.a = foo1.a; + WRITE_ONCE(foo2.b, READ_ONCE(foo1.b)); + foo2.c = foo1.c; + +Aparte de esto, nunca es necesario usar READ_ONCE() y WRITE_ONCE() en una +variable que se ha marcado como volátil. Por ejemplo, dado que 'jiffies' +está marcado como volátil, nunca es necesario usar READ_ONCE(jiffies). La +razón de esto es que READ_ONCE() y WRITE_ONCE() se implementan como +conversiones volátiles, lo que no tiene efecto cuando su argumento ya está +marcado como volátil. + +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ásicas de memoria de CPU: + +TIPO OBLIGATORIO SMP CONDICIONAL +======================= =============== =============== +GENERAL mb() smp_mb() +WRITE wmb() smp_wmb() +READ rmb() smp_rmb() +DEPEDENCIA DE DIRECCIÓN 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ún orden de compilación adicional. + +Además: en el caso de las dependencias de direcciones, se esperaría que el +compilador emita las cargas en el orden correcto (por ejemplo, `a[b]` +tendría que cargar el valor de b antes de cargar a[b]), sin embargo, no hay +garantía alguna en la especificación de C sobre que el compilador no puede +especular el valor de b (por ejemplo, es igual a 1) y carga a[b] antes que +b (ej. tmp = a[1]; if (b != 1) tmp = a[b]; ). También existe el problema de +que un compilador vuelva a cargar b después de haber cargado a[b], teniendo +así una copia más nueva de b que a[b]. Aún no se ha conseguido 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á ser +auto-consistente, y ordenará correctamente los accesos superpuestos +respecto a sí misma. Sin embargo, consulte la subsección "Guests de +máquinas 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és de ventanas E/S de memoria relajada. Estas +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ás avanzadas: + + (*) smp_store_mb(var, valor) + + Asigna el valor a la variable y luego inserta una barrera de memoria + completa después de ella. No se garantiza insertar nada más que una + barrera del compilador en una compilación UP. + + + (*) smp_mb__before_atomic(); + (*) smp_mb__after_atomic(); + + Estos se pueden usar con funciones RMW atómicas que no implican + barreras de memoria, pero donde el código necesita una barrera de + memoria. Ejemplos de funciones RMW atómicas 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ún donde se puede requerir una barrera es + cuando se usan operaciones atómicas como referencia de contador. + + Estos también se utilizan para funciones atómicas RMW bitop que no + implican una barrera de memoria (como set_bit y clear_bit). + + Como ejemplo, considere una pieza de código que marca un objeto como + muerto y luego disminuye el contador de referencias del objeto: + + obj->dead = 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ás + información. + + + (*) 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 != DEVICE_OWN) { + /* no leer los datos hasta que tengamos el descriptor */ + dma_rmb(); + + /* leer/modificar datos */ + read_data = desc->data; + desc->data = write_data; + + /* flush de modificaciones antes de la actualización de estado */ + dma_wmb(); + + /* asignar propiedad */ + desc->status = 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é coherente se hayan completado antes + escribiendo a la región MMIO. El writel_relaxed() más barato no + proporciona esta garantía y no debe utilizarse aquí. + + Consulte la subsección "Efectos de barrera de E/S del kernel" para + obtener más información sobre accesorios de E/S relajados y el archivo + Documentation/core-api/dma-api.rst para más información sobre 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és de una escritura no temporal en la región 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ás 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ón 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ón de accesos a memoria de + de escritura antes de esta macro, con los posteriores, cuando dicha + espera tenga implicaciones en el rendimiento. + +========================================= +BARRERAS DE MEMORIA IMPLÍCITAS DEL KERNEL +========================================= + +Algunas de las otras funciones en el kernel Linux implican barreras de +memoria, entre estas encontramos funciones de bloqueo y planificación +("scheduling"). + +Esta especificación es una garantía _mínima_; cualquier arquitectura +particular puede proporcionar garantías más sustanciales, pero no se puede +confiar en estas fuera de código específico de arquitectura. + + +FUNCIONES DE ADQUISICIÓN 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áforos + (*) R/W semáforos + +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ón ACQUIRE: + + Las operaciones de memoria emitidas después del ACQUIRE se completarán + después de que la operación ACQUIRE haya finalizado. + + Las operaciones de memoria emitidas antes de ACQUIRE pueden + completarse después que la operación ACQUIRE se ha completado. + + (2) Implicaciones de la operación RELEASE: + + Las operaciones de memoria emitidas antes de la RELEASE se + completarán antes de que la operación de RELEASE se haya completado. + + Las operaciones de memoria emitidas después de la RELEASE pueden + completarse antes de que la operación de RELEASE se haya completado. + + (3) Implicación de ACQUIRE vs ACQUIRE: + + Todas las operaciones ACQUIRE emitidas antes de otra operación + ACQUIRE serán completadas antes de esa operación ACQUIRE. + + (4) Implicación de ACQUIRE vs RELEASE: + + Todas las operaciones ACQUIRE emitidas antes de una operación RELEASE + serán completadas antes de la operación RELEASE. + + (5) Implicación de ACQUIRE condicional fallido: + + Ciertas variantes de bloqueo de la operación ACQUIRE pueden fallar, ya + sea debido a no poder obtener el bloqueo de inmediato, o debido a que + recibieron una señal de desbloqueo mientras dormían esperando que el + cerrojo estuviera disponible. Los fallos en cerrojos no implican + ningún 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ón crítica pueden filtrarse al interior de la sección +crítica. + +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és del ACQUIRE, y un acceso posterior a la RELEASE suceda antes +del RELEASE, y los dos accesos puedan entonces cruzarse: + + *A = a; + ACQUIRE M + RELEASE M + *B = b; + +puede ocurrir como: + + ACQUIRE M, STORE *B, STORE *A, RELEASE M + +Cuando ACQUIRE y RELEASE son bloqueo de adquisición y liberación, +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ón de la +CPU de los tramos críticos correspondientes a la RELEASE y la ACQUIRE +pueden cruzarse, de modo que: + + *A = a; + RELEASE M + ACQUIRE N + *B = b; + +puede ocurrir como: + + ACQUIRE N, STORE *B, STORE *A, RELEASE M + +Podría parecer que este nuevo orden podría introducir un punto muerto. +Sin embargo, esto no puede suceder porque si tal punto muerto amenazara +con suceder, el RELEASE simplemente se completaría, evitando así el +interbloqueo ("deadlock", punto muerto). + + ¿Por qué 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ó las operaciones, un deadlock -podría- ocurrir. + + Pero supongamos que la CPU reordenó las operaciones. En este caso, el + desbloqueo precede al bloqueo en el código ensamblador. La CPU + simplemente eligió intentar ejecutar primero la última operación de + bloqueo. Si hay un interbloqueo, esta operación de bloqueo simplemente + esperará (o tratará de dormir, pero hablaremos de eso más adelante). La + CPU eventualmente ejecutará la operación de desbloqueo (que precedió a la + operación de bloqueo en el código ensamblador), lo que desenmascará el + potencial punto muerto, permitiendo que la operación de bloqueo tenga + éxito. + + Pero, ¿y si el cerrojo es un cerrojo que duerme ("sleeplock")? En tal + caso, el código intentará entrar al scheduler, donde eventualmente + encontrará una barrera de memoria, que forzará la operación de desbloqueo + anterior para completar, nuevamente desentrañando el punto muerto. Podría + haber una carrera de desbloqueo del sueño ("sleep-unlock race"), pero la + primitiva de bloqueo necesita resolver tales carreras correctamente en + cualquier caso. + +Es posible que los cerrojos y los semáforos no proporcionen ninguna +garantía de orden en sistemas compilados en UP, por lo que no se puede +contar con tal situación para lograr realmente nada en absoluto, +especialmente con respecto a los accesos de E/S, a menos que se combinen +con operaciones de inhabilitación de interrupciones. + +Consulte también la sección "Efectos de barrera adquiriendo intra-CPU". + + +Como ejemplo, considere lo siguiente: + + *A = a; + *B = b; + ACQUIRE + *C = c; + *D = d; + RELEASE + *E = e; + *F = 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ÓN DE INTERRUPCIONES +-------------------------------------------- + +Las funciones que deshabilitan interrupciones (equivalentes a ACQUIRE) y +habilitan interrupciones (equivalentes a RELEASE) actuarán únicamente como +barrera del compilador. Por consiguiente, si la memoria o la E/S requieren +barreras en tal situación, deben ser provistas por algún otro medio. + + +FUNCIONES DE DORMIR Y DESPERTAR +------------------------------- + +Dormir y despertar son eventos marcados ("flagged") en los datos globales +que se pueden ver como una interacción 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áticamente mediante +set_current_state() después de haber alterado el estado de la tarea: + + CPU 1 + =============================== + 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én implican una barrera de memoria general después de +establecer el estado. Toda la secuencia anterior está disponible en varias +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ódigo que realiza una activación normalmente se +asemeja a algo como esto: + + evento_indicado = 1; + wake_up(&event_wait_queue); // despertar + +o: + + evento_indicado = 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) + =============================== =============================== + 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á despertando y es igual al +"current" (hilo actual) de la CPU 1. + +Para reiterar, se garantiza la ejecución de una barrera de memoria general +mediante wake_up() si algo está realmente despierto, pero de lo contrario +no existe tal garantía. Para entender esto, considere la siguiente +secuencia de eventos, donde X e Y son ambos cero inicialmente: + + CPU 1 CPU 2 + =============================== =============================== + X = 1; Y = 1; + smp_mb(); wake_up(); + LOAD Y LOAD X + +Si ocurre una reactivación ("wakeup"), una (al menos) de las dos cargas +debe ver 1. Si, por otro lado, no ocurre una reactivación, ambas cargas +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ían 1, garantizado. + +Las funciones de activación 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érminos de orden de la memoria, todas estas funciones proporcionan las +mismas garantías que un wake_up() (o más 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és de que el durmiente 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 = valor; + evento_indicado = 1; + wake_up(&event_wait_queue); + +no existe garantía de que el cambio a event_indicated sea percibido por +el durmiente de manera que venga después del cambio a my_data. En tal +circunstancia, el código en ambos lados debe sacar sus propias barreras de +memoria entre los separados accesos a datos. Por lo tanto, el durmiente +anterior debería hacer: + + set_current_state(TASK_INTERRUPTIBLE); + if (evento_indicado) { + smp_rmb(); + hacer_algo(my_data); + } + +y el que despierta debería hacer: + + my_data = value; + smp_wmb(); + evento_indicado = 1; + wake_up(&event_wait_queue); + +FUNCIONES VARIAS +---------------- + +Otras funciones que implican barreras: + + (*) schedule() y similares implican barreras completas de memoria. + + +======================================== +EFECTOS DE BARRERA ADQUIRIENDO INTRA-CPU +======================================== + +En los sistemas SMP, las primitivas de bloqueo proveen una forma más +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ÓN 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ía ocurrir: + + CPU 1 CPU 2 + =============================== =============================== + 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ía sobre en qué orden verá la CPU 3 los accesos a *A +hasta que *H ocurra, además de las restricciones impuestas por los bloqueos +separados en las distintas CPUs. Podría, por ejemplo, ver: + + *E, ACQUIRE M, ACQUIRE Q, *G, *C, *F, *A, *B, RELEASE Q, *D, *H, RELEASE M + +Pero no verá 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 + +======================================== +¿DÓNDE SE NECESITAN BARRERAS DE MEMORIA? +======================================== + +Bajo operación normal, el re-ordenamiento de una operación de memoria +generalmente no va a suponer un problema, ya que para una pieza de código +lineal de un solo subproceso seguirá pareciendo que funciona correctamente, +incluso si está en un kernel SMP. Existen, sin embargo, cuatro +circunstancias en las que reordenar definitivamente _podría_ ser un +problema: + + (*) Interacción entre procesadores. + + (*) Operaciones atómicas. + + (*) Acceso a dispositivos. + + (*) Interrupciones. + + +INTERACCIÓN ENTRE PROCESADORES +------------------------------ + +Cuando se da un sistema con más de un procesador, más de una CPU en el +sistema puede estar trabajando en el mismo conjunto de datos al mismo +tiempo. Esto puede causar problemas de sincronización, y la forma habitual +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áforo R/W. Aquí hay un proceso +de espera en cola del semáforo, en virtud de que tiene una parte de su pila +vinculada a la lista de procesos en espera del semáforo: + + 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ónde está 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áforo; + + (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áforo, 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á en la pila del proceso que espera, esto significa que si el puntero de +la tarea se borra _antes_ de que se lea el siguiente puntero de la lista, +otra CPU podría comenzar a procesar el proceso que espera y podría romper +el stack del proceso que espera antes de que la función up*() tenga la +oportunidad de leer el puntero que sigue. + +Considere entonces lo que podría suceder con la secuencia de eventos +anterior: + + CPU 1 CPU 2 + =============================== =============================== + 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ía solucionarse usando el bloqueo de semáforo, pero luego la +función down_xxx() tiene que obtener innecesariamente el spinlock +nuevamente, después 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án suceder antes de todos los accesos a memoria después +de dicha barrera con respecto a las demás CPU del sistema. _No_ garantiza +que todos los accesos a memoria antes de la barrera se completarán en el +momento en que la instrucción de la barrera en sí se complete. + +En un sistema UP, donde esto no sería un problema, la función smp_mb() es +solo una barrera del compilador, asegurándose así de que el compilador +emita las instrucciones en el orden correcto sin realmente intervenir en la +CPU. Como solo hay un CPU, la lógica de orden de dependencias de esa CPU se +encargará de todo lo demás. + + +OPERACIONES ATÓMICAS +-------------------- + +Si bien son, técnicamente, consideraciones de interacción entre +procesadores, las operaciones atómicas se destacan especialmente porque +algunas de ellas implican barreras de memoria completa y algunas otras no, +pero se confía mucho en ellos en su conjunto a lo largo del kernel. + +Consulte Documentation/atomic_t.txt para obtener más información. + + +ACCESO A DISPOSITIVOS +--------------------- + +Un driver puede ser interrumpido por su propia rutina de servicio de +interrupción y, por lo tanto, las dos partes del driver pueden interferir +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íticas sean +todas contenidas dentro la sección de interrupción desactivada en el +controlador. Mientras la interrupción del driver está ejecutando la rutina, +es posible que el "core" del controlador no se ejecute en la misma CPU y no +se permita que su interrupción vuelva a ocurrir hasta que la interrupción +actual haya sido resuelta, por lo tanto, el controlador de interrupción 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ón de +interrupción y luego se invoca el controlador de interrupción del +controlador: + + IRQ LOCALES DESACTIVADAS + writew(ADDR, 3); + writew(DATA, y); + IRQ LOCALES ACTIVADAS + + writew(ADDR, 4); + q = readw(DATA); + + +El almacenamiento en el registro de datos puede ocurrir después del segundo +almacenamiento en el registro de direcciones si las reglas de orden son lo +suficientemente relajadas: + + STORE *ADDR = 3, STORE *ADDR = 4, STORE *DATA = y, q = LOAD *DATA + +Si se relajan las reglas de orden, se debe asumir que los accesos +realizados dentro de la sección con interrupción deshabilitada pueden +filtrarse fuera de esta y pueden intercalarse con accesos realizados en una +interrupción - y viceversa - a menos que se utilicenn barreras implícita o +explícitas. + +Normalmente, esto no será un problema porque los accesos de E/S realizados +dentro de tales secciones incluirán operaciones de carga síncronas en +registros E/S estrictamente ordenados, que forman barreras de E/S +implícitas. + + +Una situación similar puede ocurrir entre una rutina de interrupción y dos +rutinas ejecutándose en separadas CPU que se comunican entre sí. Si tal +caso es probable, entonces se deben usar bloqueos de desactivación de +interrupciones para garantizar el orden. + + +===================================== + Efectos de barrera de E/S del kernel +===================================== + +La interfaz con periféricos a través de accesos de E/S es profundamente +específica para cada arquitectura y dispositivo. Por lo tanto, los drivers +que son inherentemente no portátiles pueden depender de comportamientos +específicos de sus sistemas de destino, con el fin de lograr la +sincronización de la manera más ligera posible. Para drivers que deseen ser +portátiles entre múltiples arquitecturas e implementaciones de bus, el +kernel ofrece una serie de funciones de acceso que proporcionan varios +grados de garantías de orden: + + (*) readX(), writeX(): + + Las funciones de acceso MMIO readX() y writeX() usan un puntero al + periférico al que se accede como un parámetro __iomem *. para punteros + asignados los atributos de E/S predeterminados (por ejemplo, los + devueltos por ioremap()), las garantías de orden son las siguientes: + + 1. Se ordenan todos los accesos readX() y writeX() a un mismo periférico + entre estos. Esto asegura que los registros de acceso MMIO por el + mismo subproceso de la CPU a un dispositivo en particular llegará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érico desde otro + subproceso de CPU, si emitido después de una adquisición 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érico primero esperará + a la finalización 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úfer DMA de salida asignadas por + dma_alloc_coherent() serán visibles para un motor ("engine") DMA + cuando la CPU escriba en sus registros de control MMIO, para activar + la transferencia. + + 4. Un readX() de un subproceso del CPU, desde el periférico, se + completará 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úfer DMA entrantes asignadas por + dma_alloc_coherent(), no verán datos obsoletos despué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érico, se + completará 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érico llegarán 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íficos de la arquitectura subyacente y, por lo +tanto, las garantías enumeradas anteriormente no pueden por lo general ser +aseguradas para accesos a este tipo de "mappings" (asignaciones). + + (*) readX_relaxed(), writeX_relaxed(): + + Son similares a readX() y writeX(), pero proporcionan una garantía de + orden de memoria más débil. Específicamente, no garantizan orden con + respecto al bloqueo, los accesos normales a la memoria o los bucles + delay() (es decir, los puntos 2-5 arriba) pero todavía se garantiza que + se ordenarán con respecto a otros accesos desde el mismo hilo de la CPU, + al mismo periférico, cuando se opera en punteros __iomem asignados con el + valor predeterminado para los atributos de E/S. + + (*) readsX(), writesX(): + + Los puntos de entrada readsX() y writesX() MMIO están diseñados para + acceder FIFOs mapeados en memoria y basados en registros que residen en + periféricos, que no son capaces de realizar DMA. Por tanto, sólo + proporcionan garantías de orden readX_relaxed() y writeX_relaxed(), como + se documentó anteriormente. + + (*) inX(), outX(): + + Los puntos de entrada inX() y outX() están destinados a acceder a mapas + de puertos "legacy" (antiguos) de periféricos de E/S, que pueden requerir + instrucciones especiales en algunas arquitecturas (especialmente, en + x86). El número de puerto del periférico que se está accedido se pasa + como un argumento. + + Dado que muchas arquitecturas de CPU acceden finalmente a estos + periféricos a través de un mapeo interno de memoria virtual, las + garantías de orden portátiles proporcionadas por inX() y outX() son las + mismas que las proporcionadas por readX() y writeX(), respectivamente, al + acceder a una asignación 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ón de escritura no publicada, que espera una respuesta de + finalización del periférico de E/S antes de regresar. Esto no está + garantizado por todas las arquitecturas y por lo tanto no forma parte de + la semántica de orden portátil. + + (*) insX(), outsX(): + + Como arriba, los puntos de entrada insX() y outsX() proporcionan el mismo + orden garantizado por readsX() y writesX() respectivamente, al acceder a + un mapping con los atributos de E/S predeterminados. + + (*) ioreadX(), iowriteX(): + + Estos funcionarán adecuadamente para el tipo de acceso que realmente están + haciendo, ya sea inX()/outX() o readX()/writeX(). + +Con la excepción de los puntos de entrada (insX(), outsX(), readsX() y +writesX()), todo lo anterior supone que el periférico subyacente es +little-endian y, por lo tanto, realizará operaciones de intercambio de +bytes en arquitecturas big-endian. + + +=========================================== +MODELO DE ORDEN MÍNIMO DE EJECUCIÓN ASUMIDO +=========================================== + +Debe suponerse que la CPU conceptual está débilmente ordenada, pero que +mantiene la apariencia de causalidad del programa con respecto a sí misma. +Algunas CPU (como i386 o x86_64) están más limitadas que otras (como +powerpc o frv), por lo que el caso más relajado (es decir, DEC Alpha) se +debe asumir fuera de código específico de arquitectura. + +Esto significa que se debe considerar que la CPU ejecutará su flujo de +instrucciones en el orden que se quiera - o incluso en paralelo - siempre +que si una instrucción en el flujo depende de una instrucción anterior, +entonces dicha instrucción anterior debe ser lo suficientemente completa[*] +antes de que la posterior instrucción puede proceder; en otras palabras: +siempre que la apariencia de causalidad se mantenga. + + [*] Algunas instrucciones tienen más de un efecto, como cambiar el + código de condición, cambio de registros o cambio de memoria - y + distintas instrucciones pueden depender de diferentes efectos. + +Una CPU puede también 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ía reordenar la +corriente de instrucciones de la manera que crea conveniente, nuevamente +siempre que la apariencia de causalidad se mantenga. + + +===================================== +EFECTOS DE LA MEMORIA CACHÉ DE LA CPU +===================================== + +La forma en que se perciben las operaciones de memoria caché en todo el +sistema se ve afectada, hasta cierto punto, por los cachés 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úa con otra parte del sistema a +través del caché, el sistema de memoria tiene que incluir los cachés de la +CPU y barreras de memoria, que en su mayor parte actúan en la interfaz +entre la CPU y su caché (las barreras de memoria lógicamente actúan sobre +la línea 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ó, ya que puede haber sido satisfecha dentro del propio +caché de la CPU, seguirá pareciendo como si el acceso total a la memoria +hubiera tenido lugar para las otras CPUs, ya que los mecanismos de +coherencia de caché migrarán la cacheline sobre la CPU que accede y se +propagarán los efectos en caso de conflicto. + +El núcleo de la CPU puede ejecutar instrucciones en el orden que considere +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úcleo puede colocarlos en la cola en cualquier orden que desee, y +continuar su ejecución hasta que se vea obligado a esperar que una +instrucción 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é. Esto depende de las propiedades de la ventana de memoria +a través de la cual se accede a los dispositivos y/o el uso de +instrucciones especiales de comunicación con dispositivo que pueda tener la +CPU. + + +COHERENCIA DE CACHÉ FRENTE A DMA +--------------------------------- + +No todos los sistemas mantienen coherencia de caché 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íneas de caché +"sucias" pueden residir en los cachés de varias CPU, y es posible que no +se hayan vuelto a escribir en la RAM todavía. Para hacer frente a esto, la +parte apropiada del kernel debe vaciar los bits superpuestos de caché en +cada CPU (y tal vez también invalidarlos). + +Además, los datos enviados por DMA a RAM, por un dispositivo, pueden ser +sobrescritos por líneas de caché sucias que se escriben de nuevo en la RAM +desde el caché de una CPU, después de que el dispositivo haya puesto sus +propios datos, o las líneas de caché presentes en el caché de la CPU pueden +simplemente ocultar el hecho de que la memoria RAM se haya actualizado, +hasta el momento en que la caché se descarta de la memoria caché 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é en cada CPU. + +Consulte Documentation/core-api/cachetlb.rst para obtener más información +sobre administración de la memoria caché. + + +COHERENCIA DE CACHÉ FRENTE A MMIO +--------------------------------- + +La E/S mapeada en memoria generalmente se lleva a cabo a través 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é 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é que se emitieron +anteriormente. Una barrera de memoria no es suficiente en tal caso, sino +que el caché debe ser vaciado entre la escritura de la memoria caché, y el +acceso MMIO, si los dos son de cualquier manera dependiente. + + +======================= +COSAS QUE HACEN LAS CPU +======================= + +Un programador podría dar por sentado que la CPU realizará las operaciones +de memoria exactamente en el orden especificado, de modo que si a la CPU se +entrega, por ejemplo, el siguiente fragmento de código a ejecutar: + + a = READ_ONCE(*A); + WRITE_ONCE(*B, b); + c = READ_ONCE(*C); + d = READ_ONCE(*D); + WRITE_ONCE(*E, e); + +esperarían entonces que la CPU complete la operación de memoria para cada +instrucción 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ás intrincada. Para muchas CPU y +compiladores, la anterior suposición no se sostiene porque: + + (*) es más probable que las cargas deban completarse de inmediato para + permitir progreso en la ejecución, mientras que los stores a menudo 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és 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í los costes + de configuración de transacciones (la memoria y los dispositivos PCI + pueden ambos pueden hacer esto); y + + (*) la caché de datos de la CPU puede afectar al orden, y mientras sus + mecanismos de coherencia pueden aliviar esto, una vez que el store + haya accedido al caché- no hay garantía de que la gestión de la + coherencia se propague en orden a otras CPU. + +Entonces, digamos que lo que otra CPU podría observar en el fragmento de +código 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á que sus + _propios_ accesos parecen estar correctamente ordenados, sin necesidad de +barrera de memoria. Por ejemplo con el siguiente código: + + U = READ_ONCE(*A); + WRITE_ONCE(*A, V); + WRITE_ONCE(*A, W); + X = READ_ONCE(*A); + WRITE_ONCE(*A, Y); + Z = READ_ONCE(*A); + +y asumiendo que no hay intervención de una influencia externa, se puede +suponer que el resultado final se parecerá a: + + U == el valor original de *A + X == W + Z == Y + *A == Y + +El código anterior puede hacer que la CPU genere la secuencia completa de +accesos de memoria: + + U=LOAD *A, STORE *A=V, STORE *A=W, X=LOAD *A, STORE *A=Y, Z=LOAD *A + +en ese orden, pero, sin intervención, la secuencia puede contener casi +cualquier combinación 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ía reordenar cargas +sucesivas en la misma ubicación. En tales arquitecturas, READ_ONCE() y +WRITE_ONCE() hacen lo que sea necesario para evitar esto, por ejemplo, en +Itanium los casts volátiles utilizados por READ_ONCE() y WRITE_ONCE() hacen +que GCC emita las instrucciones especiales ld.acq y st.rel +(respectivamente) que impiden dicha reordenación. + +El compilador también puede combinar, descartar o diferir elementos de la +secuencia antes incluso de que la CPU los vea. + +Por ejemplo: + + *A = V; + *A = W; + +puede reducirse a: + + *A = 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 = Y; + Z = *A; + +puede, sin una barrera de memoria o un READ_ONCE() y WRITE_ONCE(), esto +sea reducido a: + + *A = Y; + Z = Y; + +y la operación LOAD nunca aparezca fuera de la CPU. + + +Y LUEGO ESTÁ EL ALFA +-------------------- + +La CPU DEC Alpha es una de las CPU más relajadas que existen. No solo eso, +algunas versiones de la CPU Alpha tienen un caché de datos dividido, lo que +les permite tener dos líneas de caché relacionadas semánticamente, +actualizadas en momentos separados. Aquí es donde la barrera de dependencia +de dirección realmente se vuelve necesaria, ya que se sincronizan ambos +cachés con el sistema de coherencia de memoria, lo que hace que parezca 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ón al kernel de Linux de smp_mb() a READ_ONCE() en Alpha +redujo en gran medida su impacto en el modelo de memoria. + + +GUESTS DE MÁQUINAS VIRTUALES +----------------------------- + +Los "guests" (invitados) que se ejecutan en máquinas virtuales pueden verse +afectados por los efectos de SMP incluso si el "host" (huésped) en sí se +compila sin compatibilidad con SMP. Este es un efecto de la interacción con +un host SMP mientras ejecuta un kernel UP. El uso obligatorio de barreras +para este caso de uso sería posible, pero a menudo no son óptimas. + +Para hacer frente a este caso de manera óptima, están disponibles macros de +bajo nivel virt_mb() etc. Estas tienen el mismo efecto que smp_mb(), etc. +cuando SMP está habilitado, pero generan código idéntico para sistemas SMP +y no SMP. Por ejemplo, los invitados de máquinas virtuales debería usar +virt_mb() en lugar de smp_mb() al sincronizar contra un (posiblemente SMP) +anfitrión. + +Estos son equivalentes a sus contrapartes smp_mb() etc. en todos los demás +aspectos, en particular, no controlan los efectos MMIO: para controlar los +efectos MMIO, utilice barreras obligatorias. + + +================ +EJEMPLOS DE USOS +================ + +BUFFERS CIRCULARES +------------------ + +Las barreras de memoria se pueden utilizar para implementar almacenamiento +en búfer circular, sin necesidad de un cerrojo para serializar al productor +con el consumidor. Vea: + + Documentation/core-api/circular-buffers.rst + +para más detalles. + + +=========== +REFERENCIAS +=========== + +Alpha AXP Architecture Reference Manual, Segunda Edición (por Sites & Witek, +Digital Press) + Capítulo 5.2: Physical Address Space Characteristics + Capítulo 5.4: Caches and Write Buffers + Capítulo 5.5: Data Sharing + Capítulo 5.6: Read/Write Ordering + +AMD64 Architecture Programmer's Manual Volumen 2: System Programming + Capítulo 7.1: Memory-Access Ordering + Capítulo 7.4: Buffering and Combining Memory Writes + +ARM Architecture Reference Manual (ARMv8, for ARMv8-A architecture profile) + Capítulo B2: The AArch64 Application Level Memory Model + +IA-32 Intel Architecture Software Developer's Manual, Volumen 3: +System Programming Guide + Capítulo 7.1: Locked Atomic Operations + Capítulo 7.2: Memory Ordering + Capítulo 7.4: Serializing Instructions + +The SPARC Architecture Manual, Version 9 + Capítulo 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ítulo 5: Memory Accesses and Cacheability + Capítulo 15: Sparc-V9 Memory Models + +UltraSPARC III Cu User's Manual + Capítulo 9: Memory Models + +UltraSPARC IIIi Processor User's Manual + Capítulo 8: Memory Models + +UltraSPARC Architecture 2005 + Capítulo 9: Memory + Appendix D: Formal Specifications of the Memory Models + +UltraSPARC T1 Supplement to the UltraSPARC Architecture 2005 + Capítulo 8: Memory Models + Appendix F: Caches and Cache Coherency + +Solaris Internals, Core Kernel Architecture, p63-68: + Capítulo 3.3: Hardware Considerations for Locks and + Synchronization + +Unix Systems for Modern Architectures, Symmetric Multiprocessing and Caching +for Kernel Programmers: + Capítulo 13: Other Memory Models + +Intel Itanium Architecture Software Developer's Manual: Volumen 1: + Sección 2.6: Speculation + Sección 4.4: Memory Access diff --git a/Documentation/translations/sp_SP/process/adding-syscalls.rst b/Documentation/translations/sp_SP/process/adding-syscalls.rst new file mode 100644 index 0000000000..f21504c612 --- /dev/null +++ b/Documentation/translations/sp_SP/process/adding-syscalls.rst @@ -0,0 +1,632 @@ +.. include:: ../disclaimer-sp.rst + +:Original: :ref:`Documentation/process/adding-syscalls.rst ` +:Translator: Mauricio Fuentes + +.. _sp_addsyscalls: + +Agregando una Nueva Llamada del Sistema +======================================= + +Este documento describe qué involucra agregar una nueva llamada del sistema +al kernel Linux, más allá de la presentación y consejos normales en +:ref:`Documentation/process/submitting-patches.rst ` que +también puede encontrar traducido a este idioma. + +Alternativas a Llamadas del Sistema +----------------------------------- + +La primera cosa a considerar cuando se agrega una llamada al sistema es si +alguna alternativa es adecuada en su lugar. Aunque las llamadas al sistema +son los puntos de interacción entre el userspace y el kernel más obvios y +tradicionales, existen otras posibilidades -- elija la que mejor se adecúe +a su interfaz. + + - Si se puede hacer que la operación se parezca a un objeto filesystem, + podría tener más sentido crear un nuevo sistema de ficheros o + dispositivo. Esto también hará más fácil encapsular la nueva + funcionalidad en un módulo del kernel en vez de requerir que sea + construido junto al kernel principal. + + - Si la nueva funcionalidad involucra operaciones donde el kernel + notifica al userspace que algo ha pasado, entonces retornar un nuevo + descriptor de archivo para el objeto relevante permite al userspace + usar ``poll``/``select``/``epoll`` para recibir esta notificación. + + - Sin embargo, operaciones que no mapean a operaciones similares a + :manpage:`read(2)`/:manpage:`write(2)` tienen que ser implementadas + como solicitudes :manpage:`ioctl(2)`, las cuales pueden llevar a un + API algo opaca. + + - Si sólo está exponiendo información del runtime, un nuevo nodo en sysfs + (mire ``Documentation/filesystems/sysfs.rst``) o el filesystem ``/proc`` + podría ser más adecuado. Sin embargo, acceder a estos mecanismos + requiere que el filesystem relevante esté montado, lo que podría no ser + siempre el caso (e.g. en un ambiente namespaced/sandboxed/chrooted). + Evite agregar cualquier API a debugfs, ya que no se considera una + interfaz (interface) de 'producción' para el userspace. + + - Si la operación es específica a un archivo o descriptor de archivo + específico, entonces la opción de comando adicional :manpage:`fcntl(2)` + podría ser más apropiada. Sin embargo, :manpage:`fcntl(2)` es una + llamada al sistema multiplexada que esconde mucha complejidad, así que + esta opción es mejor cuando la nueva funcion es analogamente cercana a + la funcionalidad existente :manpage:`fcntl(2)`, o la nueva funcionalidad + es muy simple (por ejemplo, definir/obtener un flag simple relacionado a + un descriptor de archivo). + + - Si la operación es específica a un proceso o tarea particular, entonces + un comando adicional :manpage:`prctl(2)` podría ser más apropiado. Tal + como con :manpage:`fcntl(2)`, esta llamada al sistema es un multiplexor + complicado así que está reservado para comandos análogamente cercanos + del existente ``prctl()`` u obtener/definir un flag simple relacionado a + un proceso. + +Diseñando el API: Planeando para extensiones +-------------------------------------------- + +Una nueva llamada del sistema forma parte del API del kernel, y tiene que +ser soportada indefinidamente. Como tal, es una muy buena idea discutir +explícitamente el interface en las listas de correo del kernel, y es +importante planear para futuras extensiones del interface. + +(La tabla syscall está poblada con ejemplos históricos donde esto no se +hizo, junto con los correspondientes seguimientos de los system calls -- +``eventfd``/``eventfd2``, ``dup2``/``dup3``, ``inotify_init``/``inotify_init1``, +``pipe``/``pipe2``, ``renameat``/``renameat2`` -- así que aprenda de la +historia del kernel y planee extensiones desde el inicio.) + +Para llamadas al sistema más simples que sólo toman un par de argumentos, +la forma preferida de permitir futuras extensiones es incluir un argumento +flag a la llamada al sistema. Para asegurarse que el userspace pueda usar +de forma segura estos flags entre versiones del kernel, revise si los flags +contienen cualquier flag desconocido, y rechace la llamada al sistema (con +``EINVAL``) si ocurre:: + + if (flags & ~(THING_FLAG1 | THINGFLAG2 | THING_FLAG3)) + return -EINVAL; + +(Si no hay valores de flags usados aún, revise que los argumentos del flag +sean cero.) + +Para llamadas al sistema más sofisticadas que involucran un gran número de +argumentos, es preferible encapsular la mayoría de los argumentos en una +estructura que sea pasada a través de un puntero. Tal estructura puede +hacer frente a futuras extensiones mediante la inclusión de un argumento de +tamaño en la estructura:: + + struct xyzzy_params { + u32 size; /* userspace define p->size = sizeof(struct xyzzy_params) */ + u32 param_1; + u64 param_2; + u64 param_3; + }; + +Siempre que cualquier campo añadido subsecuente, digamos ``param_4``, sea +diseñado de forma tal que un valor cero, devuelva el comportamiento previo, +entonces permite versiones no coincidentes en ambos sentidos: + + - Para hacer frente a programas del userspace más modernos, haciendo + llamadas a un kernel más antiguo, el código del kernel debe revisar que + cualquier memoria más allá del tamaño de la estructura sea cero (revisar + de manera efectiva que ``param_4 == 0``). + - Para hacer frente a programas antiguos del userspace haciendo llamadas a + un kernel más nuevo, el código del kernel puede extender con ceros, una + instancia más pequeña de la estructura (definiendo efectivamente + ``param_4 == 0``). + +Revise :manpage:`perf_event_open(2)` y la función ``perf_copy_attr()`` (en +``kernel/events/code.c``) para un ejemplo de esta aproximación. + + +Diseñando el API: Otras consideraciones +--------------------------------------- + +Si su nueva llamada al sistema permite al userspace hacer referencia a un +objeto del kernel, esta debería usar un descriptor de archivo como el +manipulador de ese objeto -- no invente un nuevo tipo de objeto manipulador +userspace cuando el kernel ya tiene mecanismos y semánticas bien definidas +para usar los descriptores de archivos. + +Si su nueva llamada a sistema :manpage:`xyzzy(2)` retorna un nuevo +descriptor de archivo, entonces el argumento flag debe incluir un valor que +sea equivalente a definir ``O_CLOEXEC`` en el nuevo FD. Esto hace posible +al userspace acortar la brecha de tiempo entre ``xyzzy()`` y la llamada a +``fcntl(fd, F_SETFD, FD_CLOEXEC)``, donde un ``fork()`` inesperado y +``execve()`` en otro hilo podrían filtrar un descriptor al programa +ejecutado. (Sin embargo, resista la tentación de reusar el valor actual de +la constante ``O_CLOEXEC``, ya que es específica de la arquitectura y es +parte de un espacio numerado de flags ``O_*`` que está bastante lleno.) + +Si su llamada de sistema retorna un nuevo descriptor de archivo, debería +considerar también que significa usar la familia de llamadas de sistema +:manpage:`poll(2)` en ese descriptor de archivo. Hacer un descriptor de +archivo listo para leer o escribir es la forma normal para que el kernel +indique al espacio de usuario que un evento ha ocurrido en el +correspondiente objeto del kernel. + +Si su nueva llamada de sistema :manpage:`xyzzy(2)` involucra algún nombre +de archivo como argumento:: + + int sys_xyzzy(const char __user *path, ..., unsigned int flags); + +debería considerar también si una versión :manpage:`xyzzyat(2)` es mas +apropiada:: + + int sys_xyzzyat(int dfd, const char __user *path, ..., unsigned int flags); + +Esto permite más flexibilidad en como el userspace especifica el archivo en +cuestión; en particular esto permite al userspace pedir la funcionalidad a +un descriptor de archivo ya abierto usando el flag ``AT_EMPTY_PATH``, +efectivamente dando una operación :manpage:`fxyzzy(3)` gratis:: + + - xyzzyat(AT_FDCWD, path, ..., 0) es equivalente a xyzzy(path, ...) + - xyzzyat(fd, "", ..., AT_EMPTY_PATH) es equivalente a fxyzzy(fd, ...) + +(Para más detalles sobre la explicación racional de las llamadas \*at(), +revise el man page :manpage:`openat(2)`; para un ejemplo de AT_EMPTY_PATH, +mire el man page :manpage:`fstatat(2)` manpage.) + +Si su nueva llamada de sistema :manpage:`xyzzy(2)` involucra un parámetro +describiendo un describiendo un movimiento dentro de un archivo, ponga de +tipo ``loff_t`` para que movimientos de 64-bit puedan ser soportados +incluso en arquitecturas de 32-bit. + +Si su nueva llamada de sistema :manpage:`xyzzy` involucra una +funcionalidad privilegiada, esta necesita ser gobernada por la capability +bit linux apropiada (revisado con una llamada a ``capable()``), como se +describe en el man page :manpage:`capabilities(7)`. Elija una parte de +capability linux que govierne las funcionalidades relacionadas, pero trate +de evitar combinar muchas funciones sólo relacionadas vagamente bajo la +misma sección, ya que va en contra de los propósitos de las capabilities de +dividir el poder del usuario root. En particular, evite agregar nuevos usos +de la capacidad ya demasiado general de la capabilities ``CAP_SYS_ADMIN``. + +Si su nueva llamada de sistema :manpage:`xyzzy(2)` manipula un proceso que +no es el proceso invocado, este debería ser restringido (usando una llamada +a ``ptrace_may_access()``) de forma que el único proceso con los mismos +permisos del proceso objetivo, o con las capacidades (capabilities) +necesarias, pueda manipulador el proceso objetivo. + +Finalmente, debe ser conciente de que algunas arquitecturas no-x86 tienen +un manejo más sencillo si los parámetros que son explícitamente 64-bit +caigan en argumentos enumerados impares (i.e. parámetros 1,3,5), para +permitir el uso de pares contiguos de registros 32-bits. (Este cuidado no +aplica si el argumento es parte de una estructura que se pasa a través de +un puntero.) + +Proponiendo el API +------------------ + +Para hacer una nueva llamada al sistema fácil de revisar, es mejor dividir +el patchset (conjunto de parches) en trozos separados. Estos deberían +incluir al menos los siguientes items como commits distintos (cada uno de +los cuales se describirá más abajo): + + - La implementación central de la llamada al sistema, junto con + prototipos, numeración genérica, cambios Kconfig e implementaciones de + rutinas de respaldo (fallback stub) + - Conectar la nueva llamada a sistema a una arquitectura particular, + usualmente x86 (incluyendo todas las x86_64, x86_32 y x32). + - Una demostración del use de la nueva llamada a sistema en el userspace + vía un selftest en ``tools/testing/selftest/``. + - Un borrador de man-page para la nueva llamada a sistema, ya sea como + texto plano en la carta de presentación, o como un parche (separado) + para el repositorio man-pages. + +Nuevas propuestas de llamadas de sistema, como cualquier cambio al API del +kernel, debería siempre ser copiado a linux-api@vger.kernel.org. + + +Implementation de Llamada de Sistema Generica +--------------------------------------------- + +La entrada principal a su nueva llamada de sistema :manpage:`xyzzy(2)` será +llamada ``sys_xyzzy()``, pero incluya este punto de entrada con la macro +``SYSCALL_DEFINEn()`` apropiada en vez de explicitamente. El 'n' indica el +numero de argumentos de la llamada de sistema, y la macro toma el nombre de +la llamada de sistema seguida por el par (tipo, nombre) para los parámetros +como argumentos. Usar esta macro permite a la metadata de la nueva llamada +de sistema estar disponible para otras herramientas. + +El nuevo punto de entrada también necesita un prototipo de función +correspondiente en ``include/linux/syscalls.h``, marcado como asmlinkage +para calzar en la manera en que las llamadas de sistema son invocadas:: + + asmlinkage long sys_xyzzy(...); + +Algunas arquitecturas (e.g. x86) tienen sus propias tablas de syscall +específicas para la arquitectura, pero muchas otras arquitecturas comparten +una tabla de syscall genéricas. Agrega su nueva llamada de sistema a la +lista genérica agregando una entrada a la lista en +``include/uapi/asm-generic/unistd.h``:: + + #define __NR_xyzzy 292 + __SYSCALL(__NR_xyzzy, sys_xyzzy ) + +También actualice el conteo de __NR_syscalls para reflejar la llamada de +sistema adicional, y note que si multiples llamadas de sistema nuevas son +añadidas en la misma ventana unida, su nueva llamada de sistema podría +tener que ser ajustada para resolver conflictos. + +El archivo ``kernel/sys_ni.c`` provee una implementación fallback stub +(rutina de respaldo) para cada llamada de sistema, retornando ``-ENOSYS``. +Incluya su nueva llamada a sistema aquí también:: + + COND_SYSCALL(xyzzy); + +Su nueva funcionalidad del kernel, y la llamada de sistema que la controla, +debería normalmente ser opcional, así que incluya una opción ``CONFIG`` +(tipicamente en ``init/Kconfig``) para ella. Como es usual para opciones +``CONFIG`` nuevas: + + - Incluya una descripción para la nueva funcionalidad y llamada al sistema + controlada por la opción. + - Haga la opción dependiendo de EXPERT si esta debe estar escondida de los + usuarios normales. + - Haga que cualquier nuevo archivo fuente que implemente la función + dependa de la opción CONFIG en el Makefile (e.g. + ``obj-$(CONFIG_XYZZY_SYSCALL) += xyzzy.o``). + - Revise dos veces que el kernel se siga compilando con la nueva opción + CONFIG apagada. + +Para resumir, necesita un commit que incluya: + + - una opción ``CONFIG`` para la nueva función, normalmente en ``init/Kconfig`` + - ``SYSCALL_DEFINEn(xyzzy, ...)`` para el punto de entrada + - El correspondiente prototipo en ``include/linux/syscalls.h`` + - Una entrada genérica en ``include/uapi/asm-generic/unistd.h`` + - fallback stub en ``kernel/sys_ni.c`` + + +Implementación de Llamada de Sistema x86 +---------------------------------------- + +Para conectar su nueva llamada de sistema a plataformas x86, necesita +actualizar las tablas maestras syscall. Asumiendo que su nueva llamada de +sistema ni es especial de alguna manera (revise abajo), esto involucra una +entrada "común" (para x86_64 y x86_32) en +arch/x86/entry/syscalls/syscall_64.tbl:: + + 333 common xyzz sys_xyzzy + +y una entrada "i386" en ``arch/x86/entry/syscalls/syscall_32.tbl``:: + + 380 i386 xyzz sys_xyzzy + +De nuevo, estos número son propensos de ser cambiados si hay conflictos en +la ventana de integración relevante. + + +Compatibilidad de Llamadas de Sistema (Genérica) +------------------------------------------------ + +Para la mayoría de llamadas al sistema la misma implementación 64-bit puede +ser invocada incluso cuando el programa de userspace es en si mismo 32-bit; +incluso si los parámetros de la llamada de sistema incluyen un puntero +explícito, esto es manipulado de forma transparente. + +Sin embargo, existe un par de situaciones donde se necesita una capa de +compatibilidad para lidiar con las diferencias de tamaño entre 32-bit y +64-bit. + +La primera es si el kernel 64-bit también soporta programas del userspace +32-bit, y por lo tanto necesita analizar areas de memoria del (``__user``) +que podrían tener valores tanto 32-bit como 64-bit. En particular esto se +necesita siempre que un argumento de la llamada a sistema es: + + - un puntero a un puntero + - un puntero a un struc conteniendo un puntero (por ejemplo + ``struct iovec __user *``) + - un puntero a un type entero de tamaño entero variable (``time_t``, + ``off_t``, ``long``, ...) + - un puntero a un struct conteniendo un type entero de tamaño variable. + +La segunda situación que requiere una capa de compatibilidad es cuando uno +de los argumentos de la llamada a sistema tiene un argumento que es +explícitamente 64-bit incluso sobre arquitectura 32-bit, por ejemplo +``loff_t`` o ``__u64``. En este caso, el valor que llega a un kernel 64-bit +desde una aplicación de 32-bit se separará en dos valores de 32-bit, los +que luego necesitan ser reensamblados en la capa de compatibilidad. + +(Note que un argumento de una llamada a sistema que sea un puntero a un +type explicitamente de 64-bit **no** necesita una capa de compatibilidad; +por ejemplo, los argumentos de :manpage:`splice(2)`) del tipo +``loff_t __user *`` no significan la necesidad de una llamada a sistema +``compat_``.) + +La versión compatible de la llamada de sistema se llama +``compat_sys_xyzzy()``, y se agrega con la macro +``COMPAT_SYSCALL_DEFINEn``, de manera análoga a SYSCALL_DEFINEn. Esta +versión de la implementación se ejecuta como parte de un kernel de 64-bit, +pero espera recibir parametros con valores 32-bit y hace lo que tenga que +hacer para tratar con ellos. (Típicamente, la versión ``compat_sys_`` +convierte los valores a versiones de 64 bits y llama a la versión ``sys_`` +o ambas llaman a una función de implementación interna común.) + +El punto de entrada compat también necesita un prototipo de función +correspondiente, en ``include/linux/compat.h``, marcado como asmlinkage +para igualar la forma en que las llamadas al sistema son invocadas:: + + asmlinkage long compat_sys_xyzzy(...); + +Si la nueva llamada al sistema involucra una estructura que que se dispone +de forma distinta en sistema de 32-bit y 64-bit, digamos +``struct xyzzy_args``, entonces el archivo de cabecera +include/linux/compat.h también debería incluir una versión compatible de la +estructura (``struct compat_xyzzy_args``) donde cada campo de tamaño +variable tiene el tipo ``compat_`` apropiado que corresponde al tipo en +``struct xyzzy_args``. La rutina ``compat_sys_xyzzy()`` puede entonces usar +esta estructura ``compat_`` para analizar los argumentos de una invocación +de 32-bit. + +Por ejemplo, si hay campos:: + + struct xyzzy_args { + const char __user *ptr; + __kernel_long_t varying_val; + u64 fixed_val; + /* ... */ + }; + +en struct xyzzy_args, entonces struct compat_xyzzy_args debe tener:: + + struct compat_xyzzy_args { + compat_uptr_t ptr; + compat_long_t varying_val; + u64 fixed_val; + /* ... */ + }; + +la lista genérica de llamadas al sistema también necesita ajustes para +permitir la versión compat; la entrada en +``include/uapi/asm-generic/unistd.h`` debería usar ``__SC_COMP`` en vez de +``__SYSCALL``:: + + #define __NR_xyzzy 292 + __SC_COMP(__NR_xyzzy, sys_xyzzy, compat_sys_xyzzy) + +Para resumir, necesita: + + - una ``COMPAT_SYSCALL_DEFINEn(xyzzy, ...)`` para el punto de entrada de compat. + - el prototipo correspondiente en ``include/linux/compat.h`` + - (en caso de ser necesario) un struct de mapeo de 32-bit en ``include/linux/compat.h`` + - una instancia de ``__SC_COMP`` no ``__SYSCALL`` en ``include/uapi/asm-generic/unistd.h`` + +Compatibilidad de Llamadas de Sistema (x86) +------------------------------------------- + +Para conectar la arquitectura x86 de una llamada al sistema con una versión +de compatibilidad, las entradas en las tablas de syscall deben ser +ajustadas. + +Primero, la entrada en ``arch/x86/entry/syscalls/syscall_32.tbl`` recibe +una columna extra para indicar que un programa del userspace de 32-bit +corriendo en un kernel de 64-bit debe llegar al punto de entrada compat:: + + 380 i386 xyzzy sys_xyzzy __ia32_compat_sys_xyzzy + +Segundo, tienes que averiguar qué debería pasar para la versión x32 ABI de +la nueva llamada al sistema. Aquí hay una elección: el diseño de los +argumentos debería coincidir con la versión de 64-bit o la versión de +32-bit. + +Si hay involucrado un puntero-a-puntero, la decisión es fácil: x32 es +ILP32, por lo que el diseño debe coincidir con la versión 32-bit, y la +entrada en ``arch/x86/entry/syscalls/syscall_64.tbl`` se divide para que +progamas 32-bit lleguen al envoltorio de compatibilidad:: + + 333 64 xyzzy sys_xyzzy + ... + 555 x32 xyzzy __x32_compat_sys_xyzzy + +Si no hay punteros involucrados, entonces es preferible reutilizar el system +call 64-bit para el x32 ABI (y consecuentemente la entrada en +arch/x86/entry/syscalls/syscall_64.tbl no se cambia). + +En cualquier caso, debes revisar que lo tipos involucrados en su diseño de +argumentos de hecho asigne exactamente de x32 (-mx32) a 32-bit(-m32) o +equivalentes 64-bit (-m64). + + +Llamadas de Sistema Retornando a Otros Lugares +---------------------------------------------- + +Para la mayoría de las llamadas al sistema, una vez que se la llamada al +sistema se ha completado el programa de usuario continúa exactamente donde +quedó -- en la siguiente instrucción, con el stack igual y la mayoría de +los registros igual que antes de la llamada al sistema, y con el mismo +espacio en la memoria virtual. + +Sin embargo, unas pocas llamadas al sistema hacen las cosas diferente. +Estas podrían retornar a una ubicación distinta (``rt_sigreturn``) o +cambiar el espacio de memoria (``fork``/``vfork``/``clone``) o incluso de +arquitectura (``execve``/``execveat``) del programa. + +Para permitir esto, la implementación del kernel de la llamada al sistema +podría necesitar guardar y restaurar registros adicionales al stak del +kernel, brindandole control completo de donde y cómo la ejecución continúa +después de la llamada a sistema. + +Esto es arch-specific, pero típicamente involucra definir puntos de entrada +assembly que guardan/restauran registros adicionales e invocan el punto de +entrada real de la llamada a sistema. + +Para x86_64, esto es implementado como un punto de entrada ``stub_xyzzy`` +en ``arch/x86/entry/entry_64.S``, y la entrada en la tabla syscall +(``arch/x86/entry/syscalls/syscall_32.tbl``) es ajustada para calzar:: + + 333 common xyzzy stub_xyzzy + +El equivalente para programas 32-bit corriendo en un kernel 64-bit es +normalmente llamado ``stub32_xyzzy`` e implementado en +``arch/x86/entry/entry_64_compat.S``, con el correspondiente ajuste en la +tabla syscall en ``arch/x86/syscalls/syscall_32.tbl``:: + + 380 i386 xyzzy sys_xyzzy stub32_xyzzy + +Si la llamada a sistema necesita una capa de compatibilidad (como en la +sección anterior) entonces la versión ``stub32_`` necesita llamar a la +versión ``compat_sys_`` de la llamada a sistema, en vez de la versión +nativa de 64-bit. También, si la implementación de la versión x32 ABI no es +comun con la versión x86_64, entonces su tabla syscall también necesitará +invocar un stub que llame a la versión ``compat_sys_`` + +Para completar, también es agradable configurar un mapeo de modo que el +user-mode linux todavía funcione -- su tabla syscall referenciará +stub_xyzzy, pero el UML construido no incluye una implementación +``arch/x86/entry/entry_64.S``. Arreglar esto es tan simple como agregar un +#define a ``arch/x86/um/sys_call_table_64.c``:: + + #define stub_xyzzy sys_xyzzy + + +Otros detalles +-------------- + +La mayoría del kernel trata las llamadas a sistema de manera genérica, pero +está la excepción ocasional que pueda requerir actualización para su +llamada a sistema particular. + +El subsistema de auditoría es un caso especial; este incluye funciones +(arch-specific) que clasifican algunos tipos especiales de llamadas al +sistema -- específicamente file open (``open``/``openat``), program +execution (``execve`` /``execveat``) o operaciones multiplexores de socket +(``socketcall``). Si su nueva llamada de sistema es análoga a alguna de +estas, entonces el sistema auditor debe ser actualizado. + +Más generalmente, si existe una llamada al sistema que sea análoga a su +nueva llamada al sistema, entonces vale la pena hacer un grep a todo el +kernel de la llamada a sistema existente, para revisar que no exista otro +caso especial. + + +Testing +------- + +Una nueva llamada al sistema debe obviamente ser probada; también es útil +proveer a los revisores con una demostración de cómo los programas del +userspace usarán la llamada al sistema. Una buena forma de combinar estos +objetivos es incluir un simple programa self-test en un nuevo directorio +bajo ``tools/testing/selftests/``. + +Para una nueva llamada al sistema, obviamente no habrá una función +envoltorio libc por lo que el test necesitará ser invocado usando +``syscall()``; también, si la llamada al sistema involucra una nueva +estructura userspace-visible, el encabezado correspondiente necesitará ser +instalado para compilar el test. + +Asegure que selftest corra satisfactoriamente en todas las arquitecturas +soportadas. Por ejemplo, revise si funciona cuando es compilado como un +x86_64 (-m64), x86_32 (-m32) y x32 (-mx32) programa ABI. + +Para pruebas más amplias y exhautivas de la nueva funcionalidad, también +debería considerar agregar tests al Linus Test Project, o al proyecto +xfstests para cambios filesystem-related + + - https://linux-test-project.github.io/ + - git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git + + +Man Page +-------- + +Todas las llamada al sistema nueva deben venir con un man page completo, +idealmente usando groff markup, pero texto plano también funciona. Si se +usa groff, es útil incluir una versión ASCII pre-renderizada del man-page +en el cover del email para el patchset, para la conveniencia de los +revisores. + +El man page debe ser cc'do a linux-man@vger.kernel.org +Para más detalles, revise https://www.kernel.org/doc/man-pages/patches.html + + +No invoque las llamadas de sistemas en el kernel +------------------------------------------------ + +Las llamadas al sistema son, cómo se declaró más arriba, puntos de +interacción entre el userspace y el kernel. Por lo tanto, las funciones de +llamada al sistema como ``sys_xyzzy()`` o ``compat_sys_xyzzy()`` deberían +ser llamadas sólo desde el userspace vía la tabla de syscall, pero no de +otro lugar en el kernel. Si la funcionalidad syscall es útil para ser usada +dentro del kernel, necesita ser compartida entre syscalls nuevas o +antiguas, o necesita ser compartida entre una syscall y su variante de +compatibilidad, esta debería ser implementada mediante una función "helper" +(como ``ksys_xyzzy()``). Esta función del kernel puede ahora ser llamada +dentro del syscall stub (``sys_xyzzy()``), la syscall stub de +compatibilidad (``compat_sys_xyzzy()``), y/o otro código del kernel. + +Al menos en 64-bit x86, será un requerimiento duro desde la v4.17 en +adelante no invocar funciones de llamada al sistema (system call) en el +kernel. Este usa una convención de llamada diferente para llamadas al +sistema donde ``struct pt_regs`` es decodificado on-the-fly en un +envoltorio syscall que luego entrega el procesamiento al syscall real. Esto +significa que sólo aquellos parámetros que son realmente necesarios para +una syscall específica son pasados durante la entrada del syscall, en vez +de llenar en seis registros de CPU con contenido random del userspace todo +el tiempo (los cuales podrían causar serios problemas bajando la cadena de +llamadas). + +Más aún, reglas sobre cómo se debería acceder a la data pueden diferir +entre la data del kernel y la data de usuario. Esta es otra razón por la +cual llamar a ``sys_xyzzy()`` es generalmente una mala idea. + +Excepciones a esta regla están permitidas solamente en overrides +específicos de arquitectura, envoltorios de compatibilidad específicos de +arquitectura, u otro código en arch/. + + +Referencias y fuentes +--------------------- + + - Artículo LWN de Michael Kerrisk sobre el uso de argumentos flags en llamadas al + sistema: + https://lwn.net/Articles/585415/ + - Artículo LWN de Michael Kerrisk sobre cómo manejar flags desconocidos en una + llamada al sistema: https://lwn.net/Articles/588444/ + - Artículo LWN de Jake Edge describiendo restricciones en argumentos en + 64-bit system call: https://lwn.net/Articles/311630/ + - Par de artículos LWN de David Drysdale que describen la ruta de implementación + de llamadas al sistema en detalle para v3.14: + + - https://lwn.net/Articles/604287/ + - https://lwn.net/Articles/604515/ + + - Requerimientos arquitectura-específicos para llamadas al sistema son discutidos en el + :manpage:`syscall(2)` man-page: + http://man7.org/linux/man-pages/man2/syscall.2.html#NOTES + - Recopilación de emails de Linus Torvalds discutiendo problemas con ``ioctl()``: + https://yarchive.net/comp/linux/ioctl.html + - "How to not invent kernel interfaces", Arnd Bergmann, + https://www.ukuug.org/events/linux2007/2007/papers/Bergmann.pdf + - Artículo LWN de Michael Kerrisk sobre evitar nuevos usos de CAP_SYS_ADMIN: + https://lwn.net/Articles/486306/ + - Recomendaciones de Andrew Morton que toda la información relacionada a una nueva + llamada al sistema debe venir en el mismo hilo de correos: + https://lore.kernel.org/r/20140724144747.3041b208832bbdf9fbce5d96@linux-foundation.org + - Recomendaciones de Michael Kerrisk que una nueva llamada al sistema debe venir + con un man-page: https://lore.kernel.org/r/CAKgNAkgMA39AfoSoA5Pe1r9N+ZzfYQNvNPvcRN7tOvRb8+v06Q@mail.gmail.com + - Sugerencias de Thomas Gleixner que conexiones x86 deben ir en commits + separados: https://lore.kernel.org/r/alpine.DEB.2.11.1411191249560.3909@nanos + - Sugerencias de Greg Kroah-Hartman que es bueno para las nueva llamadas al sistema + que vengan con man-page y selftest: https://lore.kernel.org/r/20140320025530.GA25469@kroah.com + - Discusión de Michael Kerrisk de nuevas system call vs. extensiones :manpage:`prctl(2)`: + https://lore.kernel.org/r/CAHO5Pa3F2MjfTtfNxa8LbnkeeU8=YJ+9tDqxZpw7Gz59E-4AUg@mail.gmail.com + - Sugerencias de Ingo Molnar que llamadas al sistema que involucran múltiples + argumentos deben encapsular estos argumentos en una estructura, la cual incluye + un campo de tamaño para futura extensibilidad: https://lore.kernel.org/r/20150730083831.GA22182@gmail.com + - Enumerando rarezas por la (re-)utilización de O_* numbering space flags: + + - commit 75069f2b5bfb ("vfs: renumber FMODE_NONOTIFY and add to uniqueness + check") + - commit 12ed2e36c98a ("fanotify: FMODE_NONOTIFY and __O_SYNC in sparc + conflict") + - commit bb458c644a59 ("Safer ABI for O_TMPFILE") + + - Discusión de Matthew Wilcox sobre las restricciones en argumentos 64-bit: + https://lore.kernel.org/r/20081212152929.GM26095@parisc-linux.org + - Recomendaciones de Greg Kroah-Hartman sobre flags desconocidos deben ser + vigilados: https://lore.kernel.org/r/20140717193330.GB4703@kroah.com + - Recomendaciones de Linus Torvalds que las llamadas al sistema x32 deben favorecer + compatibilidad con versiones 64-bit sobre versiones 32-bit: + https://lore.kernel.org/r/CA+55aFxfmwfB7jbbrXxa=K7VBYPfAvmu3XOkGrLbB1UFjX1+Ew@mail.gmail.com diff --git a/Documentation/translations/sp_SP/process/code-of-conduct.rst b/Documentation/translations/sp_SP/process/code-of-conduct.rst new file mode 100644 index 0000000000..adc6c770cc --- /dev/null +++ b/Documentation/translations/sp_SP/process/code-of-conduct.rst @@ -0,0 +1,97 @@ +.. include:: ../disclaimer-sp.rst + +:Original: :ref:`Documentation/process/code-of-conduct.rst ` +:Translator: Contributor Covenant and Carlos Bilbao + +.. _sp_code_of_conduct: + +Código de Conducta para Contribuyentes ++++++++++++++++++++++++++++++++++++++++ + +Nuestro Compromiso +================== + +Nosotros, como miembros, contribuyentes y administradores nos comprometemos +a hacer de la participación en nuestra comunidad una experiencia libre de +acoso para todo el mundo, independientemente de la edad, dimensión corporal, +minusvalía visible o invisible, etnicidad, características sexuales, +identidad y expresión de género, nivel de experiencia, educación, nivel +socio-económico, nacionalidad, apariencia personal, raza, religión, o +identidad u orientación sexual. Nos comprometemos a actuar e interactuar de +maneras que contribuyan a una comunidad abierta, acogedora, diversa, +inclusiva y sana. + +Nuestros Estándares +=================== + +Ejemplos de comportamiento que contribuyen a crear un ambiente positivo +para nuestra comunidad: + +* Demostrar empatía y amabilidad ante otras personas +* Respeto a diferentes opiniones, puntos de vista y experiencias +* Dar y aceptar adecuadamente retroalimentación constructiva +* Aceptar la responsabilidad y disculparse ante quienes se vean afectados + por nuestros errores, aprendiendo de la experiencia +* Centrarse en lo que sea mejor no sólo para nosotros como individuos, sino + para la comunidad en general + + +Ejemplos de comportamiento inaceptable: + +* El uso de lenguaje o imágenes sexualizadas, y aproximaciones o + atenciones sexuales de cualquier tipo +* Comentarios despectivos (trolling), insultantes o derogatorios, y ataques + personales o políticos +* El acoso en público o privado +* Publicar información privada de otras personas, tales como direcciones + físicas o de correo + electrónico, sin su permiso explícito +* Otras conductas que puedan ser razonablemente consideradas como + inapropiadas en un entorno profesional + + +Aplicación de las responsabilidades +=================================== + +Los administradores de la comunidad son responsables de aclarar y hacer +cumplir nuestros estándares de comportamiento aceptable y tomarán acciones +apropiadas y correctivas de forma justa en respuesta a cualquier +comportamiento que consideren inapropiado, amenazante, ofensivo o dañino. + +Los administradores de la comunidad tendrán el derecho y la responsabilidad +de eliminar, editar o rechazar comentarios, commits, código, ediciones de +páginas de wiki, issues y otras contribuciones que no se alineen con este +Código de Conducta, y comunicarán las razones para sus decisiones de +moderación cuando sea apropiado. + +Alcance +======= + +Este código de conducta aplica tanto a espacios del proyecto como a +espacios públicos donde un individuo esté en representación del proyecto o +comunidad. Ejemplos de esto incluyen el uso de la cuenta oficial de correo +electrónico, publicaciones a través de las redes sociales oficiales, o +presentaciones con personas designadas en eventos en línea o no. + +Aplicación +========== + +Instancias de comportamiento abusivo, acosador o inaceptable de otro modo +podrán ser reportadas contactando el Code of Conduct Commitee a través de +. Todas las quejas serán evaluadas e investigadas de +una manera puntual y justa. El Code of Condut Commitee está obligados a +respetar la privacidad y la seguridad de quienes reporten incidentes. +Detalles de políticas y aplicación en particular, serán incluidos por +separado. + +Atribución +========== + +Este Código de Conducta es una adaptación del Contributor Covenant, versión +1.4, disponible en https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +Interpretación +============== + +Consulte el documento :ref:`code_of_conduct_interpretation` para ver cómo +interpretará la comunidad del kernel Linux este documento. diff --git a/Documentation/translations/sp_SP/process/coding-style.rst b/Documentation/translations/sp_SP/process/coding-style.rst new file mode 100644 index 0000000000..a0261ba5b9 --- /dev/null +++ b/Documentation/translations/sp_SP/process/coding-style.rst @@ -0,0 +1,1315 @@ +.. include:: ../disclaimer-sp.rst + +:Original: :ref:`Documentation/process/coding-style.rst ` +:Translator: Carlos Bilbao + +.. _sp_codingstyle: + +Estilo en el código del kernel Linux +===================================== + +Este es un breve documento que describe el estilo preferido en el código +del kernel Linux. El estilo de código es muy personal y no **forzaré** mi +puntos de vista sobre nadie, pero esto vale para todo lo que tengo que +mantener, y preferiría que para la mayoría de otras cosas también. Por +favor, por lo menos considere los argumentos expuestos aquí. + +En primer lugar, sugeriría imprimir una copia de los estándares de código +GNU, y NO leerlo. Quémelos, es un gran gesto simbólico. + +De todos modos, aquí va: + + +1) Sangría +----------- + +Las tabulaciones tienen 8 caracteres y, por lo tanto, las sangrías también +tienen 8 caracteres. Hay movimientos heréticos que intentan hacer sangría +de 4 (¡o incluso 2!) caracteres de longitud, y eso es similar a tratar de +definir el valor de PI como 3. + +Justificación: La idea detrás de la sangría es definir claramente dónde +comienza y termina un bloque de control. Especialmente, cuando ha estado +buscando en su pantalla durante 20 horas seguidas, le resultará mucho más +fácil ver cómo funciona la sangría si tiene sangrías grandes. + +Bueno, algunas personas dirán que tener sangrías de 8 caracteres hace que +el código se mueva demasiado a la derecha y dificulta la lectura en una +pantalla de terminal de 80 caracteres. La respuesta a eso es que si +necesita más de 3 niveles de sangría, está en apuros de todos modos y +debería arreglar su programa. + +En resumen, las sangrías de 8 caracteres facilitan la lectura y tienen la +ventaja añadida de advertirle cuando está anidando sus funciones demasiado +profundo. Preste atención a esa advertencia. + +La forma preferida de facilitar múltiples niveles de sangría en una +declaración de switch es para alinear el ``switch`` y sus etiquetas +``case`` subordinadas en la misma columna, en lugar de hacer ``doble +sangría`` (``double-indenting``) en etiquetas ``case``. Por ejemplo: + +.. code-block:: c + + switch (suffix) { + case 'G': + case 'g': + mem <<= 30; + break; + case 'M': + case 'm': + mem <<= 20; + break; + case 'K': + case 'k': + mem <<= 10; + fallthrough; + default: + break; + } + +No ponga varias declaraciones en una sola línea a menos que tenga algo que +ocultar: + +.. code-block:: c + + if (condición) haz_esto; + haz_otra_cosa; + +No use comas para evitar el uso de llaves: + +.. code-block:: c + + if (condición) + haz_esto(), haz_eso(); + +Siempre use llaves para múltiples declaraciones: + +.. code-block:: c + + if (condición) { + haz_esto(); + haz_eso(); + } + +Tampoco ponga varias asignaciones en una sola línea. El estilo de código +del kernel es súper simple. Evite las expresiones engañosas. + + +Aparte de los comentarios, la documentación y excepto en Kconfig, los +espacios nunca se utilizan para la sangría, y el ejemplo anterior se rompe +deliberadamente. + +Consiga un editor decente y no deje espacios en blanco al final de las +líneas. + +2) Rompiendo líneas y strings largos +------------------------------------ + +El estilo de código tiene todo que ver con la legibilidad y la +mantenibilidad usando herramientas disponibles comúnmente. + +El límite preferido en la longitud de una sola línea es de 80 columnas. + +Las declaraciones de más de 80 columnas deben dividirse en partes, a menos +que exceder las 80 columnas aumente significativamente la legibilidad y no +oculte información. + +Los descendientes siempre son sustancialmente más cortos que el padre y +se colocan sustancialmente a la derecha. Un estilo muy usado es alinear +descendientes a un paréntesis de función abierto. + +Estas mismas reglas se aplican a los encabezados de funciones con una larga +lista de argumentos. + +Sin embargo, nunca rompa los strings visibles para el usuario, como los +mensajes printk, porque eso rompe la capacidad de grep a estos. + + +3) Colocación de llaves y espacios +---------------------------------- + +El otro problema que siempre surge en el estilo C es la colocación de +llaves. A diferencia del tamaño de la sangría, existen pocas razones +técnicas para elegir una estrategia de ubicación sobre la otra, pero la +forma preferida, como mostraron los profetas Kernighan y Ritchie, es poner +la llave de apertura en la línea, y colocar la llave de cierre primero, +así: + +.. code-block:: c + + if (x es verdad) { + hacemos y + } + +Esto se aplica a todos los bloques de declaraciones que no son funciones +(if, switch, for, while, do). Por ejemplo: + +.. code-block:: c + + switch (action) { + case KOBJ_ADD: + return "add"; + case KOBJ_REMOVE: + return "remove"; + case KOBJ_CHANGE: + return "change"; + default: + return NULL; + } + +Sin embargo, hay un caso especial, a saber, las funciones: tienen la llave +de apertura al comienzo de la siguiente línea, así: + +.. code-block:: c + + int funcion(int x) + { + cuerpo de la función + } + +Gente hereje de todo el mundo ha afirmado que esta inconsistencia es... +bueno... inconsistente, pero todas las personas sensatas saben que +(a) K&R tienen **razón** y (b) K&R tienen razón. Además, las funciones son +especiales de todos modos (no puede anidarlas en C). + +Tenga en cuenta que la llave de cierre está vacía en su línea propia, +**excepto** en los casos en que es seguida por una continuación de la misma +declaración, es decir, un ``while`` en una sentencia do o un ``else`` en +una sentencia if, como en: + +.. code-block:: c + + do { + cuerpo del bucle do + } while (condition); + +y + +.. code-block:: c + + if (x == y) { + .. + } else if (x > y) { + ... + } else { + .... + } + +Justificación: K&R. + +Además, tenga en cuenta que esta colocación de llaves también minimiza el +número de líneas vacías (o casi vacías), sin pérdida de legibilidad. Así, +como el suministro de nuevas líneas en su pantalla no es un recurso +renovable (piense en pantallas de terminal de 25 líneas), tienes más líneas +vacías para poner comentarios. + +No use llaves innecesariamente donde una sola declaración sea suficiente. + +.. code-block:: c + + if (condition) + accion(); + +y + +.. code-block:: none + + if (condición) + haz_esto(); + else + haz_eso(); + +Esto no aplica si solo una rama de una declaración condicional es una sola +declaración; en este último caso utilice llaves en ambas ramas: + +.. code-block:: c + + if (condición) { + haz_esto(); + haz_eso(); + } else { + en_otro_caso(); + } + +Además, use llaves cuando un bucle contenga más de una declaración simple: + +.. code-block:: c + + while (condición) { + if (test) + haz_eso(); + } + +3.1) Espacios +************* + +El estilo del kernel Linux para el uso de espacios depende (principalmente) +del uso de función versus uso de palabra clave. Utilice un espacio después +de (la mayoría de) las palabras clave. Las excepciones notables son sizeof, +typeof, alignof y __attribute__, que parecen algo así como funciones (y +generalmente se usan con paréntesis en Linux, aunque no son requeridos en +el idioma, como en: ``sizeof info`` después de que ``struct fileinfo info;`` +se declare). + +Así que use un espacio después de estas palabras clave:: + + if, switch, case, for, do, while + +pero no con sizeof, typeof, alignof, o __attribute__. Por ejemplo, + +.. code-block:: c + + + s = sizeof(struct file); + +No agregue espacios alrededor (dentro) de expresiones entre paréntesis. +Este ejemplo es **malo**: + +.. code-block:: c + + + s = sizeof( struct file ); + +Al declarar datos de puntero o una función que devuelve un tipo de puntero, +el uso preferido de ``*`` es adyacente al nombre del dato o nombre de la +función y no junto al nombre del tipo. Ejemplos: + +.. code-block:: c + + + char *linux_banner; + unsigned long long memparse(char *ptr, char **retptr); + char *match_strdup(substring_t *s); + +Use un espacio alrededor (a cada lado de) la mayoría de los operadores +binarios y ternarios, como cualquiera de estos:: + + = + - < > * / % | & ^ <= >= == != ? : + +pero sin espacio después de los operadores unarios:: + + & * + - ~ ! sizeof typeof alignof __attribute__ defined + +sin espacio antes de los operadores unarios de incremento y decremento del +sufijo:: + + ++ -- + +y sin espacio alrededor de los operadores de miembros de estructura ``.`` y +``->``. + +No deje espacios en blanco al final de las líneas. Algunos editores con +``inteligente`` sangría insertarán espacios en blanco al comienzo de las +nuevas líneas como sea apropiado, para que pueda comenzar a escribir la +siguiente línea de código de inmediato. Sin embargo, algunos de estos +editores no eliminan los espacios en blanco si finalmente no termina +poniendo una línea de código allí, como si dejara una línea en blanco. Como +resultado, termina con líneas que contienen espacios en blanco al final. + +Git le advertirá sobre los parches que introducen espacios en blanco al +final y puede, opcionalmente, eliminar los espacios en blanco finales por +usted; sin embargo, si se aplica una serie de parches, esto puede hacer que +los parches posteriores de la serie fallen al cambiar sus líneas de +contexto. + + +4) Nomenclatura +--------------- + +C es un lenguaje espartano, y sus convenciones de nomenclatura deberían +seguir su ejemplo. A diferencia de los programadores de Modula-2 y Pascal, +los programadores de C no usan nombres cuquis como +EstaVariableEsUnContadorTemporal. Un programador de C lo llamaría +variable ``tmp``, que es mucho más fácil de escribir, y no es mas difícil +de comprender. + +SIN EMBARGO, mientras que los nombres de mayúsculas y minúsculas están mal +vistos, los nombres descriptivos para las variables globales son +imprescindibles. Llamar a una función global ``foo`` es un delito. + +Una variable GLOBAL (para usar solo si **realmente** las necesita) necesita +tener un nombre descriptivo, al igual que las funciones globales. Si tiene +una función que cuenta el número de usuarios activos, debe llamar a esta +``contar_usuarios_activos()`` o similar, **no** debe llamarlo ``cntusr()``. + +Codificar el tipo de una función en el nombre (lo llamado notación húngara) +es estúpido: el compilador conoce los tipos de todos modos y puede +verificar estos, y solo confunde al programador. + +Los nombres de las variables LOCALES deben ser breves y directos. Si usted +tiene algún contador aleatorio de tipo entero, probablemente debería +llamarse ``i``. Llamarlo ``loop_counter`` no es productivo, si no hay +posibilidad de ser mal entendido. De manera similar, ``tmp`` puede ser casi +cualquier tipo de variable que se utiliza para contener un valor temporal. + +Si tiene miedo de mezclar los nombres de las variables locales, tiene otro +problema, que se denomina síndrome de +función-crecimiento-desequilibrio-de-hormona. Vea el capítulo 6 (Funciones). + +Para nombres de símbolos y documentación, evite introducir nuevos usos de +'master / slave' (maestro / esclavo) (o 'slave' independientemente de +'master') y 'lista negra / lista blanca' (backlist / whitelist). + +Los reemplazos recomendados para 'maestro / esclavo' son: + '{primary,main} / {secondary,replica,subordinate}' + '{initiator,requester} / {target,responder}' + '{controller,host} / {device,worker,proxy}' + 'leader / follower' + 'director / performer' + +Los reemplazos recomendados para 'backlist / whitelist' son: + 'denylist / allowlist' + 'blocklist / passlist' + +Las excepciones para la introducción de nuevos usos son mantener en espacio +de usuario una ABI/API, o al actualizar la especificación del código de un +hardware o protocolo existente (a partir de 2020) que requiere esos +términos. Para nuevas especificaciones, traduzca el uso de la terminología +de la especificación al estándar de código del kernel donde sea posible. + +5) Typedefs +----------- + +Por favor no use cosas como ``vps_t``. +Es un **error** usar typedef para estructuras y punteros. cuando ve un + +.. code-block:: c + + + vps_t a; + +en el código fuente, ¿qué significa? +En cambio, si dice + +.. code-block:: c + + struct virtual_container *a; + +puede decir qué es ``a`` en realidad. + +Mucha gente piensa que los typedefs ``ayudan a la legibilidad``. No. Son +útiles solamente para: + + (a) objetos totalmente opacos (donde el typedef se usa activamente para + **ocultar** cuál es el objeto). + + Ejemplo: ``pte_t`` etc. objetos opacos a los que solo puede acceder + usando las funciones de acceso adecuadas. + + .. note:: + + La opacidad y las ``funciones de acceso`` no son buenas por sí + mismas. La razón por la que los tenemos para cosas como pte_t, etc. + es que hay real y absolutamente **cero** información accesible de + forma portátil allí. + + (b) Tipos enteros claros, donde la abstracción **ayuda** a evitar + confusiones, ya sea ``int`` o ``long``. + + u8/u16/u32 son definiciones tipográficas perfectamente correctas + aunque encajan en la categoría (d) mejor que aquí. + + .. note:: + + De nuevo - debe haber una **razón** para esto. si algo es + ``unsigned long``, entonces no hay razón para hacerlo + + typedef unsigned long mis_flags_t; + + pero si hay una razón clara de por qué bajo ciertas circunstancias + podría ser un ``unsigned int`` y bajo otras configuraciones podría + ser ``unsigned long``, entonces, sin duda, adelante y use un typedef. + + (c) cuando lo use para crear literalmente un tipo **nuevo** para + comprobación de tipos. + + (d) Nuevos tipos que son idénticos a los tipos estándar C99, en ciertas + circunstancias excepcionales. + + Aunque sólo costaría un corto período de tiempo para los ojos y + cerebro para acostumbrarse a los tipos estándar como ``uint32_t``, + algunas personas se oponen a su uso de todos modos. + + Por lo tanto, los tipos ``u8/u16/u32/u64`` específicos de Linux y sus + equivalentes con signo, que son idénticos a los tipos estándar son + permitidos, aunque no son obligatorios en el nuevo código de su + elección. + + Al editar código existente que ya usa uno u otro conjunto de tipos, + debe ajustarse a las opciones existentes en ese código. + + (e) Tipos seguros para usar en el espacio de usuario. + + En ciertas estructuras que son visibles para el espacio de usuario, no + podemos requerir tipos C99 y o utilizat el ``u32`` anterior. Por lo + tanto, usamos __u32 y tipos similares en todas las estructuras que se + comparten con espacio de usuario. + +Tal vez también haya otros casos, pero la regla básicamente debería ser +NUNCA JAMÁS use un typedef a menos que pueda coincidir claramente con una +de estas reglas. + +En general, un puntero o una estructura que tiene elementos que pueden +ser razonablemente accedidos directamente, **nunca** deben ser un typedef. + +6) Funciones +------------ + +Las funciones deben ser cortas y dulces, y hacer una sola cosa. Deberían +caber en una o dos pantallas de texto (el tamaño de pantalla ISO/ANSI es +80x24, como todos sabemos), y hacer una cosa y hacerla bien. + +La longitud máxima de una función es inversamente proporcional a la +complejidad y el nivel de sangría de esa función. Entonces, si tiene una +función conceptualmente simple que es solo una larga (pero simple) +declaración de case, donde tiene que hacer un montón de pequeñas cosas para +un montón de diferentes casos, está bien tener una función más larga. + +Sin embargo, si tiene una función compleja y sospecha que un estudiante de +primer año de secundaria menos que dotado podría no comprender de qué se +trata la función, debe adherirse a los límites máximos tanto más de +cerca. Use funciones auxiliares con nombres descriptivos (puede pedirle al +compilador que los alinee si cree que es crítico para el rendimiento, y +probablemente lo hará mejor de lo que usted hubiera hecho). + +Otra medida de la función es el número de variables locales. Estas no deben +exceder de 5 a 10, o está haciendo algo mal. Piense de nuevo en la función +y divida en partes más pequeñas. Un cerebro humano puede generalmente +realiza un seguimiento de aproximadamente 7 cosas diferentes, cualquier +elemento más y se confunde. Usted sabe que es brillante, pero tal vez le +gustaría entender lo que hizo dentro de 2 semanas. + +En los archivos fuente, separe las funciones con una línea en blanco. Si la +función es exportada, la macro **EXPORT** debería ponerse inmediatamente +después de la función de cierre de línea de llave. Por ejemplo: + +.. code-block:: c + + int sistema_corriendo(void) + { + return estado_sistema == SISTEMA_CORRIENDO; + } + EXPORT_SYMBOL(sistema_corriendo); + +6.1) Prototipos de funciones +**************************** + +En los prototipos de funciones, incluya nombres de parámetros con sus tipos +de datos. Aunque esto no es requerido por el lenguaje C, se prefiere en +Linux porque es una forma sencilla de añadir información valiosa para el +lector. + +No utilice la palabra clave ``extern`` con declaraciones de función ya que +esto hace las líneas más largas y no es estrictamente necesario. + +Al escribir prototipos de funciones, mantenga el `orden de los elementos regular +`_. +Por ejemplo, usando este ejemplo de declaración de función:: + + __init void * __must_check action(enum magic value, size_t size, u8 count, + char *fmt, ...) __printf(4, 5) __malloc; + +El orden preferido de elementos para un prototipo de función es: + +- clase de almacenamiento (a continuación, ``static __always_inline``, + teniendo en cuenta que ``__always_inline`` es técnicamente un atributo + pero se trata como ``inline``) +- atributos de clase de almacenamiento (aquí, ``__init`` -- es decir, + declaraciones de sección, pero también cosas como ``__cold``) +- tipo de retorno (aquí, ``void *``) +- atributos de tipo de retorno (aquí, ``__must_check``) +- nombre de la función (aquí, ``action``) +- parámetros de la función (aquí, ``(enum magic value, size_t size, u8 count, char *fmt, ...)``, + teniendo en cuenta que los nombres de los parámetros siempre deben + incluirse) +- atributos de parámetros de función (aquí, ``__printf(4, 5)``) +- atributos de comportamiento de la función (aquí, ``__malloc``) + +Tenga en cuenta que para una **definición** de función (es decir, el cuerpo +real de la función), el compilador no permite atributos de parámetros de +función después de parámetros de la función. En estos casos, deberán ir +tras los atributos de clase (por ejemplo, tenga en cuenta el cambio de +posición de ``__printf(4, 5)`` a continuación, en comparación con el +ejemplo de **declaración** anterior):: + + static __always_inline __init __printf(4, 5) void * __must_check action(enum magic value, + size_t size, u8 count, char *fmt, ...) __malloc + { + ... + } + +7) Salida centralizada de funciones +----------------------------------- + +Aunque desaprobado por algunas personas, el equivalente de la instrucción +goto es utilizado con frecuencia por los compiladores, en forma de +instrucción de salto incondicional. + +La declaración goto es útil cuando una función sale desde múltiples +ubicaciones y se deben realizar algunos trabajos comunes, como la limpieza. +Si no se necesita limpieza, entonces simplemente haga return directamente. + +Elija nombres de etiquetas que digan qué hace el goto o por qué existe el +goto. Un ejemplo de un buen nombre podría ser ``out_free_buffer:`` +(``salida_liberar_buffer``) si al irse libera ``buffer``. Evite usar +nombres GW-BASIC como ``err1:`` y ``err2:``, ya que tendría que volver a +numerarlos si alguna vez agrega o elimina rutas de salida, y hacen que sea +difícil de verificar que sean correctos, de todos modos. + +La razón para usar gotos es: + +- Las declaraciones incondicionales son más fáciles de entender y seguir. +- se reduce el anidamiento +- errores al no actualizar los puntos de salida individuales al hacer + modificaciones son evitados +- ahorra el trabajo del compilador de optimizar código redundante ;) + +.. code-block:: c + + int fun(int a) + { + int result = 0; + char *buffer; + + buffer = kmalloc(SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + if (condition1) { + while (loop1) { + ... + } + result = 1; + goto out_free_buffer; + } + ... + out_free_buffer: + kfree(buffer); + return result; + } + +Un tipo común de error a tener en cuenta es "un error de error" que es algo +así: + +.. code-block:: c + + err: + kfree(foo->bar); + kfree(foo); + return ret; + +El error en este código es que en algunas rutas de salida, ``foo`` es NULL. +Normalmente la solución para esto es dividirlo en dos etiquetas de error +``err_free_bar:`` y ``err_free_foo:``: + +.. code-block:: c + + err_free_bar: + kfree(foo->bar); + err_free_foo: + kfree(foo); + return ret; + +Idealmente, debería simular errores para probar todas las rutas de salida. + + +8) Comentarios +-------------- + +Los comentarios son buenos, pero también existe el peligro de comentar +demasiado. NUNCA trate de explicar CÓMO funciona su código en un +comentario: es mucho mejor escribir el código para que el +**funcionamiento** sea obvio y es una pérdida de tiempo explicar código mal +escrito. + +Generalmente, desea que sus comentarios digan QUÉ hace su código, no CÓMO. +Además, trate de evitar poner comentarios dentro del cuerpo de una función: +si la función es tan compleja que necesita comentar por separado partes de +esta, probablemente debería volver al capítulo 6 una temporada. Puede +hacer pequeños comentarios para notar o advertir sobre algo particularmente +inteligente (o feo), pero trate de evitar el exceso. En su lugar, ponga los +comentarios al principio de la función, diga a la gente lo que hace y +posiblemente POR QUÉ hace esto. + +Al comentar las funciones de la API del kernel, utilice el formato +kernel-doc. Consulte los archivos en :ref:`Documentation/doc-guide/ ` +y ``scripts/kernel-doc`` para más detalles. + +El estilo preferido para comentarios largos (de varias líneas) es: + +.. code-block:: c + + /* + * Este es el estilo preferido para comentarios + * multilínea en el código fuente del kernel Linux. + * Por favor, utilícelo constantemente. + * + * Descripción: Una columna de asteriscos en el lado izquierdo, + * con líneas iniciales y finales casi en blanco. + */ + +Para archivos en net/ y drivers/net/, el estilo preferido para comentarios +largos (multi-linea) es un poco diferente. + +.. code-block:: c + + /* El estilo de comentario preferido para archivos en net/ y drivers/net + * se asemeja a esto. + * + * Es casi lo mismo que el estilo de comentario generalmente preferido, + * pero no hay una línea inicial casi en blanco. + */ + +También es importante comentar los datos, ya sean tipos básicos o +derivados. Para este fin, use solo una declaración de datos por línea (sin +comas para múltiples declaraciones de datos). Esto le deja espacio para un +pequeño comentario sobre cada elemento, explicando su uso. + +9) Has hecho un desastre +--------------------------- + +Está bien, todos lo hacemos. Probablemente un antiguo usuario de Unix le +haya dicho que ``GNU emacs`` formatea automáticamente las fuentes C por +usted, y ha notado que sí, lo hace, pero los por defecto que tiene son +menos que deseables (de hecho, son peores que los aleatorios) escribiendo - +un número infinito de monos escribiendo en GNU emacs nunca harán un buen +programa). + +Por lo tanto, puede deshacerse de GNU emacs o cambiarlo y usar valores más +sanos. Para hacer esto último, puede pegar lo siguiente en su archivo +.emacs: + +.. code-block:: none + + (defun c-lineup-arglist-tabs-only (ignored) + "Line up argument lists by tabs, not spaces" + (let* ((anchor (c-langelem-pos c-syntactic-element)) + (column (c-langelem-2nd-pos c-syntactic-element)) + (offset (- (1+ column) anchor)) + (steps (floor offset c-basic-offset))) + (* (max steps 1) + c-basic-offset))) + + (dir-locals-set-class-variables + 'linux-kernel + '((c-mode . ( + (c-basic-offset . 8) + (c-label-minimum-indentation . 0) + (c-offsets-alist . ( + (arglist-close . c-lineup-arglist-tabs-only) + (arglist-cont-nonempty . + (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) + (arglist-intro . +) + (brace-list-intro . +) + (c . c-lineup-C-comments) + (case-label . 0) + (comment-intro . c-lineup-comment) + (cpp-define-intro . +) + (cpp-macro . -1000) + (cpp-macro-cont . +) + (defun-block-intro . +) + (else-clause . 0) + (func-decl-cont . +) + (inclass . +) + (inher-cont . c-lineup-multi-inher) + (knr-argdecl-intro . 0) + (label . -1000) + (statement . 0) + (statement-block-intro . +) + (statement-case-intro . +) + (statement-cont . +) + (substatement . +) + )) + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + )))) + + (dir-locals-set-directory-class + (expand-file-name "~/src/linux-trees") + 'linux-kernel) + +Esto hará que emacs funcione mejor con el estilo de código del kernel para +C en archivos bajo ``~/src/linux-trees``. + +Pero incluso si no logra que emacs realice un formateo correcto, no todo +está perdido: use ``indent``. + +Ahora bien, de nuevo, la sangría de GNU tiene la misma configuración de +muerte cerebral que GNU emacs tiene, por lo que necesita darle algunas +opciones de línea de comando. Sin embargo, eso no es tan malo, porque +incluso los creadores de GNU indent reconocen la autoridad de K&R (la gente +de GNU no es mala, solo están gravemente equivocados en este asunto), por +lo que simplemente de a la sangría las opciones ``-kr -i8`` (significa +``K&R, guiones de 8 caracteres``), o use ``scripts/Lindent``, que indenta +con ese estilo. + +``indent`` tiene muchas opciones, y especialmente cuando se trata de +comentar reformateos, es posible que desee echar un vistazo a la página del +manual. Pero recuerde: ``indent`` no es la solución para una mala +programación. + +Tenga en cuenta que también puede usar la herramienta ``clang-format`` para +ayudarlo con estas reglas, para volver a formatear rápidamente partes de su +código automáticamente, y revisar archivos completos para detectar errores +de estilo del código, errores tipográficos y posibles mejoras. También es +útil para ordenar ``#includes``, para alinear variables/macros, para +redistribuir texto y otras tareas similares. Vea el archivo +:ref:`Documentation/process/clang-format.rst ` para más +detalles. + +10) Archivos de configuración de Kconfig +---------------------------------------- + +Para todos los archivos de configuración de Kconfig* en todo el árbol +fuente, la sangría es algo diferente. Las líneas bajo una definición +``config`` están indentadas con una tabulación, mientras que el texto de +ayuda tiene una sangría adicional de dos espacios. Ejemplo:: + + config AUDIT + bool "Soporte para auditar" + depends on NET + help + Habilita la infraestructura de auditoría que se puede usar con otro + subsistema kernel, como SELinux (que requiere esto para + registro de salida de mensajes avc). No hace auditoría de llamadas al + sistema sin CONFIG_AUDITSYSCALL. + +Características seriamente peligrosas (como soporte de escritura para +ciertos filesystems) deben anunciar esto de forma destacada en su cadena de +solicitud:: + + config ADFS_FS_RW + bool "ADFS write support (DANGEROUS)" + depends on ADFS_FS + ... + +Para obtener la documentación completa sobre los archivos de configuración, +consulte el archivo Documentation/kbuild/kconfig-language.rst. + + +11) Estructuras de datos +------------------------ + +Las estructuras de datos que tienen visibilidad fuera del contexto de un +solo subproceso en el que son creadas y destruidas, siempre debe tener +contadores de referencia. En el kernel, la recolección de basura no existe +(y fuera, la recolección de basura del kernel es lenta e ineficiente), lo +que significa que absolutamente **tiene** para hacer referencia y contar +todos sus usos. + +El conteo de referencias significa que puede evitar el bloqueo y permite +que múltiples usuarios tengan acceso a la estructura de datos en paralelo - +y no tengan que preocuparse de que la estructura, de repente, desaparezca +debajo de su control, solo porque durmieron o hicieron otra cosa por un +tiempo. + +Tenga en cuenta que el bloqueo **no** reemplaza el recuento de referencia. +El bloqueo se utiliza para mantener la coherencia de las estructuras de +datos, mientras que la referencia y contar es una técnica de gestión de +memoria. Por lo general, ambos son necesarios, y no deben confundirse entre +sí. + +De hecho, muchas estructuras de datos pueden tener dos niveles de conteo de +referencias, cuando hay usuarios de diferentes ``clases``. El conteo de +subclases cuenta el número de usuarios de la subclase y disminuye el conteo +global solo una vez, cuando el recuento de subclases llega a cero. + +Se pueden encontrar ejemplos de este tipo de ``recuento de referencias de +niveles múltiples`` en la gestión de memoria (``struct mm_struct``: +mm_users y mm_count), y en código del sistema de archivos +(``struct super_block``: s_count y s_active). + +Recuerde: si otro hilo puede encontrar su estructura de datos y usted no +tiene un recuento de referencias, es casi seguro que tiene un error. + +12) Macros, Enums y RTL +------------------------ + +Los nombres de macros que definen constantes y etiquetas en enumeraciones +(enums) están en mayúsculas. + +.. code-block:: c + + #define CONSTANTE 0x12345 + +Se prefieren los enums cuando se definen varias constantes relacionadas. + +Se aprecian los nombres de macro en MAYÚSCULAS, pero las macros que se +asemejan a funciones puede ser nombradas en minúscula. + +Generalmente, las funciones en línea son preferibles a las macros que se +asemejan a funciones. + +Las macros con varias instrucciones deben contenerse en un bloque do-while: + +.. code-block:: c + + #define macrofun(a, b, c) \ + do { \ + if (a == 5) \ + haz_esto(b, c); \ + } while (0) + +Cosas a evitar al usar macros: + +1) macros que afectan el flujo de control: + +.. code-block:: c + + #define FOO(x) \ + do { \ + if (blah(x) < 0) \ + return -EBUGGERED; \ + } while (0) + +es una **muy** mala idea. Parece una llamada de función pero sale de la +función de ``llamada``; no rompa los analizadores internos de aquellos que +leerán el código. + +2) macros que dependen de tener una variable local con un nombre mágico: + +.. code-block:: c + + #define FOO(val) bar(index, val) + +puede parecer algo bueno, pero es confuso como el infierno cuando uno lee +el código, y es propenso a romperse por cambios aparentemente inocentes. + +3) macros con argumentos que se usan como valores l: FOO(x) = y; le van +a morder si alguien, por ejemplo, convierte FOO en una función en línea. + +4) olvidarse de la precedencia: las macros que definen constantes usando +expresiones deben encerrar la expresión entre paréntesis. Tenga cuidado con +problemas similares con macros usando parámetros. + +.. code-block:: c + + #define CONSTANTE 0x4000 + #define CONSTEXP (CONSTANTE | 3) + +5) colisiones de espacio de nombres ("namespace") al definir variables +locales en macros que se asemejan a funciones: + +.. code-block:: c + + #define FOO(x) \ + ({ \ + typeof(x) ret; \ + ret = calc_ret(x); \ + (ret); \ + }) + +ret es un nombre común para una variable local -es menos probable que +__foo_ret colisione (coincida) con una variable existente. + +El manual de cpp trata las macros de forma exhaustiva. El manual interno de +gcc también cubre RTL, que se usa frecuentemente con lenguaje ensamblador +en el kernel. + +13) Imprimir mensajes del kernel +-------------------------------- + +A los desarrolladores del kernel les gusta ser vistos como alfabetizados. +Cuide la ortografía de los mensajes del kernel para causar una buena +impresión. No utilice contracciones incorrectas como ``dont``; use +``do not`` o ``don't`` en su lugar. Haga sus mensajes concisos, claros e +inequívocos. + +Los mensajes del kernel no tienen que terminar con un punto. + +Imprimir números entre paréntesis (%d) no agrega valor y debe evitarse. + +Hay varias modelos de macros de diagnóstico de driver en +que debe usar para asegurarse de que los mensajes coincidan con el +dispositivo correcto y driver, y están etiquetados con el nivel correcto: +dev_err(), dev_warn(), dev_info(), y así sucesivamente. Para mensajes que +no están asociados con un dispositivo particular, define +pr_notice(), pr_info(), pr_warn(), pr_err(), etc. + +Crear buenos mensajes de depuración puede ser todo un desafío; y una vez +los tiene, pueden ser de gran ayuda para la resolución remota de problemas. +Sin embargo, la impresión de mensajes de depuración se maneja de manera +diferente a la impresión de otros mensajes que no son de depuración. +Mientras que las otras funciones pr_XXX() se imprimen incondicionalmente, +pr_debug() no lo hace; se compila fuera por defecto, a menos que DEBUG sea +definido o se establezca CONFIG_DYNAMIC_DEBUG. Eso es cierto para dev_dbg() +también, y una convención relacionada usa VERBOSE_DEBUG para agregar +mensajes dev_vdbg() a los ya habilitados por DEBUG. + +Muchos subsistemas tienen opciones de depuración de Kconfig para activar +-DDEBUG en el Makefile correspondiente; en otros casos, los archivos +usan #define DEBUG. Y cuando un mensaje de depuración debe imprimirse +incondicionalmente, por ejemplo si es ya dentro de una sección #ifdef +relacionada con la depuración, printk(KERN_DEBUG ...) puede ser usado. + +14) Reservando memoria +---------------------- + +El kernel proporciona los siguientes asignadores de memoria de propósito +general: kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc() y +vzalloc(). Consulte la documentación de la API para obtener más información. +a cerca de ellos. :ref:`Documentation/core-api/memory-allocation.rst +` + +La forma preferida para pasar el tamaño de una estructura es la siguiente: + +.. code-block:: c + + p = kmalloc(sizeof(*p), ...); + +La forma alternativa donde se deletrea el nombre de la estructura perjudica +la legibilidad, y presenta una oportunidad para un error cuando se cambia +el tipo de variable de puntero, pero el tamaño correspondiente de eso que +se pasa a un asignador de memoria no. + +Convertir el valor devuelto, que es un puntero vacío, es redundante. La +conversión desde el puntero vacío a cualquier otro tipo de puntero está +garantizado por la programación en idioma C. + +La forma preferida para asignar una matriz es la siguiente: + +.. code-block:: c + + p = kmalloc_array(n, sizeof(...), ...); + +La forma preferida para asignar una matriz a cero es la siguiente: + +.. code-block:: c + + p = kcalloc(n, sizeof(...), ...); + +Ambos casos verifican el desbordamiento en el tamaño de asignación n * +sizeof (...), y devuelven NULL si esto ocurrió. + +Todas estas funciones de asignación genéricas emiten un volcado de pila +(" stack dump") en caso de fallo cuando se usan sin __GFP_NOWARN, por lo +que no sirve de nada emitir un mensaje de fallo adicional cuando se +devuelva NULL. + +15) La enfermedad de inline +---------------------------- + +Parece haber una común percepción errónea de que gcc tiene una magica +opción "hazme más rápido" de aceleración, llamada ``inline`` (en línea). +Mientras que el uso de inlines puede ser apropiado (por ejemplo, como un +medio para reemplazar macros, consulte el Capítulo 12), muy a menudo no lo +es. El uso abundante de la palabra clave inline conduce a una mayor kernel, +que a su vez ralentiza el sistema en su conjunto, debido a una mayor huella +de icache para la CPU, y sencillamente porque hay menos memoria disponible +para el pagecache. Solo piense en esto; un fallo en la memoria caché de la +página provoca una búsqueda de disco, que tarda fácilmente 5 milisegundos. +Hay MUCHOS ciclos de CPU que puede entrar en estos 5 milisegundos. + +Una razonable regla general es no poner funciones inline que tengan más de +3 líneas de código en ellas. Una excepción a esta regla son los casos en +que se sabe que un parámetro es una constante en tiempo de compilación, y +como resultado de esto, usted *sabe*, el compilador podrá optimizar la +mayor parte de su función en tiempo de compilación. Para un buen ejemplo de +este último caso, véase la función en línea kmalloc(). + +A menudo, la gente argumenta que agregar funciones en línea que son +estáticas y se usan solo una vez, es siempre una victoria ya que no hay +perdida de espacio. Mientras esto es técnicamente correcto, gcc es capaz de +incorporarlos automáticamente sin ayuda, y esta el problema de +mantenimiento de eliminar el inline, cuando un segundo usuario supera el +valor potencial de la pista que le dice a gcc que haga algo que habría +hecho de todos modos. + +16) Valores devueltos por función y sus nombres +----------------------------------------------- + +Las funciones pueden devolver valores de muchos tipos diferentes, y uno de +lo más común es un valor que indica si la función tuvo éxito o ha fallado. +Dicho valor se puede representar como un número entero de código de error +(-Exxx = falla, 0 = éxito) o un booleano ``con éxito`` (0 = falla, distinto +de cero = éxito). + +La mezcla de estos dos tipos de representaciones es una fuente fértil de +errores difíciles de encontrar. Si el lenguaje C incluyera una fuerte +distinción entre enteros y booleanos, el compilador encontraría estos +errores por nosotros... pero no lo hace. Para ayudar a prevenir tales +errores, siga siempre esta convención:: + + Si el nombre de una función es una acción o un comando imperativo, + la función debe devolver un número entero de código de error. si el nombre + es un predicado, la función debe devolver un valor booleano "exitoso". + +Por ejemplo, ``agregar trabajo`` es un comando, y la función +agregar_trabajo() devuelve 0 en caso de éxito o -EBUSY en caso de fracaso. +De la misma manera, ``dispositivo PCI presente`` es un predicado, y la +función pci_dev_present() devuelve 1 si tiene éxito en encontrar un +dispositivo coincidente o 0 si no es así. + +Todas las funciones EXPORTed (exportadas) deben respetar esta convención, +al igual que todas las funciones publicas. Las funciones privadas +(estáticas) no lo necesitan, pero es recomendado que lo hagan. + +Las funciones cuyo valor devuelto es el resultado real de un cálculo, en +lugar de una indicación de si el cómputo tuvo éxito, no están sujetas a +esta regla. Generalmente indican fallo al devolver valores fuera del rango +de resultados. Los ejemplos típicos serían funciones que devuelven +punteros; estos usan NULL o el mecanismo ERR_PTR para informar de fallos. + +17) Usando bool +---------------- + +El tipo bool del kernel Linux es un alias para el tipo C99 _Bool. Los +valores booleanos pueden solo evaluar a 0 o 1, y la conversión implícita o +explícita a bool convierte automáticamente el valor en verdadero o falso. +Cuando se utilizan tipos booleanos, +!! no se necesita construcción, lo que elimina una clase de errores. + +Cuando se trabaja con valores booleanos, se deben usar las definiciones +verdadera y falsa, en lugar de 1 y 0. + +Los tipos de devolución de función bool y las variables de pila siempre +se pueden usar cuando esto sea adecuado. Se recomienda el uso de bool para +mejorar la legibilidad y, a menudo, es una mejor opción que 'int' para +almacenar valores booleanos. + +No use bool si el diseño de la línea de caché o el tamaño del valor son +importantes, ya que su tamaño y la alineación varía según la arquitectura +compilada. Las estructuras que son optimizadas para la alineación y el +tamaño no debe usar bool. + +Si una estructura tiene muchos valores verdadero/falso, considere +consolidarlos en un bitfield con miembros de 1 bit, o usando un tipo de +ancho fijo apropiado, como u8. + +De manera similar, para los argumentos de función, se pueden consolidar +muchos valores verdaderos/falsos en un solo argumento bit a bit 'flags' y +'flags' a menudo, puede ser una alternativa de argumento más legible si los +sitios de llamada tienen constantes desnudas de tipo verdaderas/falsas. + +De lo contrario, el uso limitado de bool en estructuras y argumentos puede +mejorar la legibilidad. + +18) No reinvente las macros del kernel +--------------------------------------- + +El archivo de cabecera include/linux/kernel.h contiene una serie de macros +que debe usar, en lugar de programar explícitamente alguna variante de +estos por usted mismo. Por ejemplo, si necesita calcular la longitud de una +matriz, aproveche la macro + +.. code-block:: c + + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +De manera similar, si necesita calcular el tamaño de algún miembro de la +estructura, use + +.. code-block:: c + + #define sizeof_field(t, f) (sizeof(((t*)0)->f)) + +También hay macros min() y max() que realizan una verificación estricta de +tipos si lo necesita. Siéntase libre de leer detenidamente ese archivo de +encabezado para ver qué más ya está definido y que no debe reproducir en su +código. + +19) Editores modeline y otros desastres +--------------------------------------- + +Algunos editores pueden interpretar la información de configuración +incrustada en los archivos fuente, indicado con marcadores especiales. Por +ejemplo, emacs interpreta las líneas marcadas como esto: + +.. code-block:: c + + -*- mode: c -*- + +O así: + +.. code-block:: c + + /* + Local Variables: + compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" + End: + */ + +Vim interpreta los marcadores que se ven así: + +.. code-block:: c + + /* vim:set sw=8 noet */ + +No incluya ninguno de estos en los archivos fuente. La gente tiene sus +propias configuraciones del editor, y sus archivos de origen no deben +anularlos. Esto incluye marcadores para sangría y configuración de modo. +La gente puede usar su propio modo personalizado, o puede tener algún otro +método mágico para que la sangría funcione correctamente. + + +20) Ensamblador inline +----------------------- + +En el código específico de arquitectura, es posible que deba usar +ensamblador en línea para interactuar con funcionalidades de CPU o +plataforma. No dude en hacerlo cuando sea necesario. Sin embargo, no use +ensamblador en línea de forma gratuita cuando C puede hacer el trabajo. +Puede y debe empujar el hardware desde C cuando sea posible. + +Considere escribir funciones auxiliares simples que envuelvan bits comunes +de ensamblador, en lugar de escribirlos repetidamente con ligeras +variaciones. Recuerde que el ensamblador en línea puede usar parámetros C. + +Las funciones de ensamblador grandes y no triviales deben ir en archivos .S, +con su correspondientes prototipos de C definidos en archivos de encabezado +en C. Los prototipos de C para el ensamblador deben usar ``asmlinkage``. + +Es posible que deba marcar su declaración asm como volátil, para evitar que +GCC la elimine si GCC no nota ningún efecto secundario. No siempre es +necesario hacerlo, sin embargo, y hacerlo innecesariamente puede limitar la +optimización. + +Al escribir una sola declaración de ensamblador en línea que contiene +múltiples instrucciones, ponga cada instrucción en una línea separada en +una string separada, y termine cada string excepto la última con ``\n\t`` +para indentar correctamente la siguiente instrucción en la salida en +ensamblador: + +.. code-block:: c + + asm ("magic %reg1, #42\n\t" + "more_magic %reg2, %reg3" + : /* outputs */ : /* inputs */ : /* clobbers */); + +21) Compilación condicional +--------------------------- + +Siempre que sea posible, no use condicionales de preprocesador (#if, +#ifdef) en archivos .c; de lo contrario, el código es más difícil de leer y +la lógica más difícil de seguir. En cambio, use dichos condicionales en un +archivo de encabezado que defina funciones para usar en esos archivos .c, +proporcionando versiones de código auxiliar sin operación en el caso #else, +y luego llame a estas funciones incondicionalmente desde archivos .c. El +compilador evitará generar cualquier código para las llamadas restantes, +produciendo resultados idénticos, pero la lógica es fácil de seguir. + +Prefiera compilar funciones completas, en lugar de porciones de funciones o +porciones de expresiones. En lugar de poner un ifdef en una expresión, +divida la totalidad de la expresión con una función de ayuda independiente +y aplique el condicional a esa función. + +Si tiene una función o variable que puede potencialmente quedar sin usar en +una configuración en particular, y el compilador advertiría sobre su +definición sin usar, marque la definición como __maybe_unused en lugar de +envolverla en un preprocesador condicional. (Sin embargo, si una función o +variable *siempre* acaba sin ser usada, bórrela.) + +Dentro del código, cuando sea posible, use la macro IS_ENABLED para +convertir un símbolo Kconfig en una expresión booleana de C, y utilícelo en +un condicional de C normal: + +.. code-block:: c + + if (IS_ENABLED(CONFIG_SOMETHING)) { + ... + } + +El compilador "doblará"" constantemente el condicional e incluirá o +excluirá el bloque de código al igual que con un #ifdef, por lo que esto no +agregará ningún tiempo de gastos generales en ejecución. Sin embargo, este +enfoque todavía permite que el compilador de C vea el código dentro del +bloque, y verifique que sea correcto (sintaxis, tipos, símbolo, referencias, +etc.). Por lo tanto, aún debe usar un #ifdef si el código dentro del bloque +hace referencia a símbolos que no existirán si no se cumple la condición. + +Al final de cualquier bloque #if o #ifdef no trivial (más de unas pocas +líneas), incluya un comentario después de #endif en la misma línea, +anotando la expresión condicional utilizada. Por ejemplo: + +.. code-block:: c + + #ifdef CONFIG_SOMETHING + ... + #endif /* CONFIG_SOMETHING */ + +22) No rompa el kernel +----------------------- + +En general, la decisión de romper el kernel pertenece al usuario, más que +al desarrollador del kernel. + +Evite el panic() +**************** + +panic() debe usarse con cuidado y principalmente solo durante el arranque +del sistema. panic() es, por ejemplo, aceptable cuando se queda sin memoria +durante el arranque y no puede continuar. + +Use WARN() en lugar de BUG() +**************************** + +No agregue código nuevo que use cualquiera de las variantes BUG(), como +BUG(), BUG_ON() o VM_BUG_ON(). En su lugar, use una variante WARN*(), +preferiblemente WARN_ON_ONCE(), y posiblemente con código de recuperación. +El código de recuperación no es requerido si no hay una forma razonable de +recuperar, al menos parcialmente. + +"Soy demasiado perezoso para tener en cuenta los errores" no es una excusa +para usar BUG(). Importantes corrupciones internas sin forma de continuar +aún pueden usar BUG(), pero necesitan una buena justificación. + +Use WARN_ON_ONCE() en lugar de WARN() o WARN_ON() +************************************************* + +Generalmente, se prefiere WARN_ON_ONCE() a WARN() o WARN_ON(), porque es +común que una condición de advertencia dada, si ocurre, ocurra varias +veces. Esto puede llenar el registro del kernel, e incluso puede ralentizar +el sistema lo suficiente como para que el registro excesivo se convierta en +su propio, adicional problema. + +No haga WARN a la ligera +************************ + +WARN*() está diseñado para situaciones inesperadas que nunca deberían +suceder. Las macros WARN*() no deben usarse para nada que se espera que +suceda durante un funcionamiento normal. No hay "checkeos" previos o +posteriores a la condición, por ejemplo. De nuevo: WARN*() no debe usarse +para una condición esperada que vaya a activarse fácilmente, por ejemplo, +mediante acciones en el espacio del usuario. pr_warn_once() es una +alternativa posible, si necesita notificar al usuario de un problema. + +No se preocupe sobre panic_on_warn de usuarios +********************************************** + +Algunas palabras más sobre panic_on_warn: Recuerde que ``panic_on_warn`` es +una opción disponible del kernel, y que muchos usuarios configuran esta +opción. Esta es la razón por la que hay un artículo de "No haga WARN a la +ligera", arriba. Sin embargo, la existencia de panic_on_warn de usuarios no +es una razón válida para evitar el uso juicioso de WARN*(). Esto se debe a +que quien habilita panic_on_warn, explícitamente pidió al kernel que +fallara si se dispara un WARN*(), y tales usuarios deben estar preparados +para afrontar las consecuencias de un sistema que es algo más probable que +se rompa. + +Use BUILD_BUG_ON() para aserciones en tiempo de compilación +*********************************************************** + +El uso de BUILD_BUG_ON() es aceptable y recomendado, porque es una aserción +en tiempo de compilación, que no tiene efecto en tiempo de ejecución. + +Apéndice I) Referencias +----------------------- + +The C Programming Language, Segunda edicion +por Brian W. Kernighan and Dennis M. Ritchie. +Prentice Hall, Inc., 1988. +ISBN 0-13-110362-8 (paperback), 0-13-110370-9 (hardback). + +The Practice of Programming +por Brian W. Kernighan and Rob Pike. +Addison-Wesley, Inc., 1999. +ISBN 0-201-61586-X. + +manuales GCC - en cumplimiento con K&R y este texto - para cpp, gcc, +detalles de gcc y sangría, todo disponible en https://www.gnu.org/manual/ + +WG14 es el grupo de trabajo de estandarización internacional de la +programación en lenguaje C, URL: http://www.open-std.org/JTC1/SC22/WG14/ + +:ref:`process/coding-style.rst ` del kernel, por greg@kroah.com at OLS 2002: +http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/ diff --git a/Documentation/translations/sp_SP/process/contribution-maturity-model.rst b/Documentation/translations/sp_SP/process/contribution-maturity-model.rst new file mode 100644 index 0000000000..cc052ae818 --- /dev/null +++ b/Documentation/translations/sp_SP/process/contribution-maturity-model.rst @@ -0,0 +1,120 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-sp.rst + +:Original: Documentation/process/contribution-maturity-model.rst +:Translator: Avadhut Naik + +==================================================== +Modelo de Madurez de Contribución al Kernel de Linux +==================================================== + + +Los Antecedentes +================ + +Como parte de la cumbre de mantenedores del kernel de Linux 2021, hubo +una `discusión `_ sobre los desafíos +en el reclutamiento de mantenedores del kernel, así como la sucesión de +los mantenedores. Algunas de las conclusiones de esa discusión incluyeron +que las empresas que forman parte de la comunidad del kernel de Linux +necesitan permitir que los ingenieros sean mantenedores como parte de su +trabajo, para que puedan convertirse en lideres respetados y finalmente, +en mantenedores del kernel. Para apoyar una fuente solida de talento, se +debe permitir y alentar a los desarrolladores a asumir contribuciones +upstream, como revisar los parches de otras personas, reestructurar la +infraestructura del kernel y escribir documentación. + +Con ese fin, Technical Advisory Board (TAB) de la Fundación Linux propone +este Modelo de Madurez de Contribución al Kernel de Linux. Estas +expectativas comunes para la participación con la comunidad upstream +tienen como objetivo aumentar la influencia de los desarrolladores +individuales, aumentar la colaboración de las organizaciones y mejorar +la salud general del ecosistema del kernel de Linux. + +El TAB insta a las organizaciones a evaluar continuamente su modelo de +madurez de Código Abierto y comprometerse a realizar mejoras para +alinearse con este modelo. Para ser eficaz, esta evaluación debe +incorporar la reacción de toda la organización, incluyendo la gerencia +y los desarrolladores en todos los niveles de antigüedad. En el espíritu +de Código Abierto, alentamos a las organizaciones a publicar sus +evaluaciones y planes para mejorar su participación con la comunidad +upstream. + +Nivel 0 +======= + +* A los ingenieros de software no se les permite contribuir con parches + al kernel de Linux. + +Nivel 1 +======= + +* A los ingenieros de software se les permite contribuir con parches al + kernel de Linux, ya sea como parte de sus responsabilidades de trabajo + o en su propio tiempo. + +Nivel 2 +======= + +* Se espera que los ingenieros de software contribuyan al kernel de Linux + como parte de sus responsabilidades de trabajo. +* Se proporcionará apoyo a los ingenieros de software para asistir a + conferencias relacionadas con Linux como parte de su trabajo. +* Las contribuciones de código upstream de un ingeniero de software se + considerarán en la promoción y las revisiones de rendimiento. + +Nivel 3 +======= + +* Se espera que los ingenieros de software revisen los parches (incluidos + los parches escritos por ingenieros de otras empresas) como parte de + sus responsabilidades de trabajo. +* Contribuir con presentaciones o ponencias a conferencias relacionadas + con Linux o académicas (como las organizadas por la Fundación Linux, + Usenix, ACM, etc.), se consideran parte del trabajo de un ingeniero. +* Las contribuciones a la comunidad de un ingeniero de software se + considerarán en la promoción y las revisiones de rendimiento. +* Las organizaciones informarán regularmente sobre las métricas de sus + contribuciones de código abierto y harán un seguimiento de estas + métricas a lo largo del tiempo. Estas métricas pueden publicarse + solo internamente dentro de la organización, o a discreción de la + organización, algunas o todas pueden publicarse externamente. Las + métricas que se sugieren encarecidamente incluyen: + + * El número de contribuciones al kernel upstream por equipo u + organización (por ejemplo, todas las personas que reportan a un + gerente o director o vicepresidente). + * El porcentaje de desarrolladores del kernel que han realizado + contribuciones upstream relativo al total de desarrolladores + del kernel en la organización. + * El intervalo de tiempo entre los kernels utilizados en los servidores + y/o productos de la organización y la fecha de publicación del kernel + upstream en el que se basa el kernel interno. + * El número de commits fuera del árbol de desarrollo presentes en los + kernels internos. + +Nivel 4 +======= + +* Se anima a los ingenieros de software a pasar una parte de su tiempo de + trabajo centrado en el Trabajo Ascendente, que se define como revisar + parches, servir en los comités de programas, mejorar la infraestructura + del proyecto central como escribir o mantener pruebas, reducir la deuda + de tecnología upstream, escribir documentación, etc. +* Los ingenieros de software son apoyados para ayudar a organizar + conferencias relacionadas con Linux. +* Las organizaciones considerarán los comentarios de los miembros de la + comunidad en las revisiones oficiales de rendimiento. + +Nivel 5 +======= + +* El desarrollo del kernel upstream se considera un puesto de trabajo + formal, con al menos un tercio del tiempo del ingeniero pasado a hacer + el Trabajo Ascendente. +* Las organizaciones buscarán activamente las reacciones de los miembros + de la comunidad como un factor en las revisiones oficiales de + rendimiento. +* Las organizaciones informarán regularmente internamente sobre la ratio + de trabajo upstream a trabajo enfocado en perseguir directamente los + objetivos comerciales. diff --git a/Documentation/translations/sp_SP/process/deprecated.rst b/Documentation/translations/sp_SP/process/deprecated.rst new file mode 100644 index 0000000000..d52120e0d7 --- /dev/null +++ b/Documentation/translations/sp_SP/process/deprecated.rst @@ -0,0 +1,381 @@ +.. include:: ../disclaimer-sp.rst + +:Original: :ref:`Documentation/process/deprecated.rst ` +:Translator: Sergio Gonzalez + +.. _sp_deprecated: + +============================================================================ +Interfaces obsoletos, Características del lenguaje, Atributos y Convenciones +============================================================================ + +En un mundo perfecto, sería posible convertir todas las instancias de +alguna API obsoleta en una nueva API y quitar la API anterior en un +único ciclo de desarrollo. Desafortunadamente, debido al tamaño del kernel, +la jerarquía de mantenimiento, y el tiempo, no siempre es posible hacer +estos cambios de una única vez. Esto significa que las nuevas instancias +han de ir creándose en el kernel, mientras que las antiguas se quitan, +haciendo que la cantidad de trabajo para limpiar las APIs crezca. Para +informar a los desarrolladores sobre qué ha sido declarado obsoleto y por +qué, ha sido creada esta lista como un lugar donde indicar cuando los usos +obsoletos son propuestos para incluir en el kernel. + +__deprecated +------------ +Mientras que este atributo señala visualmente que un interface ha sido +declarado obsoleto, este `no produce más avisos durante las compilaciones +`_ +porque uno de los objetivos del kernel es que compile sin avisos, y +nadie ha hecho nada para quitar estos interfaces obsoletos. Mientras +que usar `__deprecated` es sencillo para anotar una API obsoleta en +un archivo de cabecera, no es la solución completa. Dichos interfaces +deben o bien ser quitados por completo, o añadidos a este archivo para +desanimar a otros a usarla en el futuro. + +BUG() y BUG_ON() +---------------- +Use WARN() y WARN_ON() en su lugar, y gestione las condiciones de error +"imposibles" tan elegantemente como se pueda. Mientras que la familia de +funciones BUG() fueron originalmente diseñadas para actuar como una +"situación imposible", confirmar y disponer de un hilo del kernel de forma +"segura", estas funciones han resultado ser demasiado arriesgadas. (e.g. +"¿en qué orden se necesitan liberar los locks? ¿Se han restaurado sus +estados?). La popular función BUG() desestabilizará el sistema o lo romperá +totalmente, lo cual hace imposible depurarlo o incluso generar reportes de +crash. Linus tiene una `opinión muy fuerte +`_ +y sentimientos `sobre esto +`_. + +Nótese que la familia de funciones WARN() únicamente debería ser usada +en situaciones que se "esperan no sean alcanzables". Si se quiere +avisar sobre situaciones "alcanzables pero no deseadas", úsese la familia +de funciones pr_warn(). Los responsables del sistema pueden haber definido +*panic_on_warn* sysctl para asegurarse que sus sistemas no continúan +ejecutándose en presencia del condiciones "no alcanzables". (Por ejemplo, +véase commits como `este +`_.) + +Operaciones aritméticas en los argumentos de reserva de memoria +--------------------------------------------------------------- +Los cálculos dinámicos de tamaño (especialmente multiplicaciones) no +deberían realizarse en los argumentos de reserva de memoria (o similares) +debido al riesgo de desbordamiento. Esto puede llevar a valores rotando y +que se realicen reservas de memoria menores que las que se esperaban. El +uso de esas reservas puede llevar a desbordamientos en el 'heap' de memoria +y otros funcionamientos incorrectos. (Una excepción a esto son los valores +literales donde el compilador si puede avisar si estos puede desbordarse. +De todos modos, el método recomendado en estos caso es reescribir el código +como se sugiere a continuación para evitar las operaciones aritméticas en +la reserva de memoria.) + +Por ejemplo, no utilice `count * size`` como argumento, como en:: + + foo = kmalloc(count * size, GFP_KERNEL); + +En vez de eso, utilice la reserva con dos argumentos:: + + foo = kmalloc_array(count, size, GFP_KERNEL); + +Específicamente, kmalloc() puede ser sustituido con kmalloc_array(), +kzalloc() puede ser sustituido con kcalloc(). + +Si no existen funciones con dos argumentos, utilice las funciones que se +saturan, en caso de desbordamiento:: + + bar = vmalloc(array_size(count, size)); + +Otro caso común a evitar es calcular el tamaño de una estructura com +la suma de otras estructuras, como en:: + + header = kzalloc(sizeof(*header) + count * sizeof(*header->item), + GFP_KERNEL); + +En vez de eso emplee:: + + header = kzalloc(struct_size(header, item, count), GFP_KERNEL); + +.. note:: Si se usa struct_size() en una estructura que contiene un elemento + de longitud cero o un array de un único elemento como un array miembro, + por favor reescribir ese uso y cambiar a un `miembro array flexible + <#zero-length-and-one-element-arrays>`_ + + +Para otros cálculos, por favor use las funciones de ayuda: size_mul(), +size_add(), and size_sub(). Por ejemplo, en el caso de:: + + foo = krealloc(current_size + chunk_size * (count - 3), GFP_KERNEL); + +Re-escríbase, como:: + + foo = krealloc(size_add(current_size, + size_mul(chunk_size, + size_sub(count, 3))), GFP_KERNEL); + +Para más detalles, mire también array3_size() y flex_array_size(), +como también la familia de funciones relacionadas check_mul_overflow(), +check_add_overflow(), check_sub_overflow(), y check_shl_overflow(). + + +simple_strtol(), simple_strtoll(), simple_strtoul(), simple_strtoull() +---------------------------------------------------------------------- +Las funciones: simple_strtol(), simple_strtoll(), simple_strtoul(), y +simple_strtoull() explícitamente ignoran los desbordamientos, lo que puede +llevar a resultados inesperados por las funciones que las llaman. Las +funciones respectivas kstrtol(), kstrtoll(), kstrtoul(), y kstrtoull() +tienden a ser reemplazos correctos, aunque nótese que necesitarán que la +cadena de caracteres termine en NUL o en el carácter de línea nueva. + + +strcpy() +-------- +strcpy() no realiza verificaciones de los límites del buffer de destino. +Esto puede resultar en desbordamientos lineals más allá del fin del buffer, +causando todo tipo de errores. Mientras `CONFIG_FORTIFY_SOURCE=y` otras +varias opciones de compilación reducen el riesgo de usar esta función, no +hay ninguna buena razón para añadir nuevos usos de esta. El remplazo seguro +es la función strscpy(), aunque se ha de tener cuidado con cualquier caso +en el el valor retornado por strcpy() sea usado, ya que strscpy() no +devuelve un puntero a el destino, sino el número de caracteres no nulos +compilados (o el valor negativo de errno cuando se trunca la cadena de +caracteres). + +strncpy() en cadenas de caracteres terminadas en NUL +---------------------------------------------------- +El uso de strncpy() no garantiza que el buffer de destino esté terminado en +NUL. Esto puede causar varios errores de desbordamiento en lectura y otros +tipos de funcionamiento erróneo debido a que falta la terminación en NUL. +Esta función también termina la cadena de caracteres en NUL en el buffer de +destino si la cadena de origen es más corta que el buffer de destino, lo +cual puede ser una penalización innecesaria para funciones usen esta +función con cadenas de caracteres que sí están terminadas en NUL. + +Cuando se necesita que la cadena de destino sea terminada en NUL, +el mejor reemplazo es usar la función strscpy(), aunque se ha de tener +cuidado en los casos en los que el valor de strncpy() fuera usado, ya que +strscpy() no devuelve un puntero al destino, sino el número de +caracteres no nulos copiados (o el valor negativo de errno cuando se trunca +la cadena de caracteres). Cualquier caso restante que necesitase todavía +ser terminado en el caracter nulo, debería usar strscpy_pad(). + +Si una función usa cadenas de caracteres que no necesitan terminar en NUL, +debería usarse strtomem(), y el destino debería señalarse con el atributo +`__nonstring +`_ +para evitar avisos futuros en el compilador. Para casos que todavía +necesitan cadenas de caracteres que se rellenen al final con el +caracter NUL, usar strtomem_pad(). + +strlcpy() +--------- +strlcpy() primero lee por completo el buffer de origen (ya que el valor +devuelto intenta ser el mismo que el de strlen()). Esta lectura puede +sobrepasar el límite de tamaño del destino. Esto ineficiente y puede causar +desbordamientos de lectura si la cadena de origen no está terminada en el +carácter NUL. El reemplazo seguro de esta función es strscpy(), pero se ha +de tener cuidado que en los casos en lso que se usase el valor devuelto de +strlcpy(), ya que strscpy() devolverá valores negativos de erno cuando se +produzcan truncados. + +Especificación de formato %p +---------------------------- +Tradicionalmente,el uso de "%p" en el formato de cadenas de caracteres +resultaría en exponer esas direcciones en dmesg, proc, sysfs, etc. En vez +de dejar que sean una vulnerabilidad, todos los "%p" que se usan en el +kernel se imprimen como un hash, haciéndolos efectivamente inutilizables +para usarlos como direcciones de memoria. Nuevos usos de "%p" no deberían +ser añadidos al kernel. Para textos de direcciones, usar "%pS" es +mejor, ya que resulta en el nombre del símbolo. Para prácticamente el +resto de casos, mejor no usar "%p" en absoluto. + +Parafraseando las actuales `direcciones de Linus `_: + +- Si el valor "hasheado" "%p" no tienen ninguna finalidad, preguntarse si el + puntero es realmente importante. ¿Quizás se podría quitar totalmente? +- Si realmente se piensa que el valor del puntero es importante, ¿porqué + algún estado del sistema o nivel de privilegio de usuario es considerado + "especial"? Si piensa que puede justificarse (en comentarios y mensajes + del commit), de forma suficiente como para pasar el escrutinio de Linux, + quizás pueda usar el "%p", a la vez que se asegura que tiene los permisos + correspondientes. + +Si está depurando algo donde el "%p" hasheado está causando problemas, +se puede arrancar temporalmente con la opción de depuración "`no_hash_pointers +`_". + + +Arrays de longitud variable (VLAs) +---------------------------------- +Usando VLA en la pila (stack) produce un código mucho peor que los arrays +de tamaño estático. Mientras que estos errores no triviales de `rendimiento +`_ son razón suficiente +para no usar VLAs, esto además son un riesgo de seguridad. El crecimiento +dinámico del array en la pila, puede exceder la memoria restante en +el segmento de la pila. Esto podría llevara a un fallo, posible sobre-escritura +de contenido al final de la pila (cuando se construye sin +`CONFIG_THREAD_INFO_IN_TASK=y`), o sobre-escritura de la memoria adyacente +a la pila (cuando se construye sin `CONFIG_VMAP_STACK=y`). + + +Switch case fall-through implícito +---------------------------------- +El lenguaje C permite a las sentencias 'switch' saltar de un caso al +siguiente caso cuando la sentencia de ruptura "break" no aparece al final +del caso. Esto, introduce ambigüedad en el código, ya que no siempre está +claro si el 'break' que falta es intencionado o un olvido. Por ejemplo, no +es obvio solamente mirando al código si `STATE_ONE` está escrito para +intencionadamente saltar en `STATE_TWO`:: + + switch (value) { + case STATE_ONE: + do_something(); + case STATE_TWO: + do_other(); + break; + default: + WARN("unknown state"); + } + +Ya que ha habido una larga lista de defectos `debidos a declaraciones de "break" +que faltan `_, no se +permiten 'fall-through' implícitos. Para identificar 'fall-through' +intencionados, se ha adoptado la pseudo-palabra-clave macro "falltrhrough", +que expande las extensiones de gcc `__attribute__((__fallthrough__)) +`_. +(Cuando la sintaxis de C17/c18 `[[fallthrough]]` sea más comúnmente +soportadas por los compiladores de C, analizadores estáticos, e IDEs, +se puede cambiar a usar esa sintaxis para esa pseudo-palabra-clave. + +Todos los bloques switch/case deben acabar en uno de: + +* break; +* fallthrough; +* continue; +* goto