linux shell linux-kernel echo ftrace

linux - ftrace: fallo del sistema al cambiar current_tracer de function_graph a través de echo



shell linux-kernel (1)

He estado jugando con ftrace recientemente para monitorear algunas características de comportamiento de mi sistema. He estado manejando la activación / desactivación del rastreo a través de un pequeño script. Después de ejecutar el script, mi sistema se bloqueaba y se reiniciaba. Inicialmente, creí que podría haber un error con el script en sí mismo, pero desde entonces he determinado que el bloqueo y el reinicio son el resultado de echo algún rastreador a / sys / kernel / debug / tracing / current_tracer cuando current_tracer está configurado como function_graph .

Es decir, la siguiente secuencia de comandos producirá el bloqueo / reinicio:

echo "function_graph" > /sys/kernel/debug/tracing/current_tracer echo "function" > /sys/kernel/debug/tracing/current_tracer

Durning el reinicio después de la caída causada por las declaraciones de echo anteriores, veo un montón de salida que lee:

borrar inodo huérfano <inode>

Intenté reproducir este problema reemplazando el valor current_tracer de function_graph a otra cosa en un programa C:

#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int openCurrentTracer() { int fd = open("/sys/kernel/debug/tracing/current_tracer", O_WRONLY); if(fd < 0) exit(1); return fd; } int writeTracer(int fd, char* tracer) { if(write(fd, tracer, strlen(tracer)) != strlen(tracer)) { printf("Failure writing %s/n", tracer); return 0; } return 1; } int main(int argc, char* argv[]) { int fd = openCurrentTracer(); char* blockTracer = "blk"; if(!writeTracer(fd, blockTracer)) return 1; close(fd); fd = openCurrentTracer(); char* graphTracer = "function_graph"; if(!writeTracer(fd, graphTracer)) return 1; close(fd); printf("Preparing to fail!/n"); fd = openCurrentTracer(); if(!writeTracer(fd, blockTracer)) return 1; close(fd); return 0; }

Curiosamente, el programa C no falla mi sistema.

Originalmente encontré este problema al usar Ubuntu (entorno Unity) 16.04 LTS y confirmé que era un problema en los kernels 4.4.0 y 4.5.5. También he probado este problema en una máquina que ejecuta Ubuntu (entorno Mate) 15.10, en los kernels 4.2.0 y 4.5.5, pero no pude reproducir el problema. Esto solo me ha confundido aún más.

¿Alguien puede darme una idea de lo que está pasando? Específicamente, ¿por qué podría write() pero no hacer echo en / sys / kernel / debug / tracing / current_tracer?

Actualizar

Como señaló vielmetti, otros han tenido un problema similar (como se ve here ).

ftrace_disable_ftrace_graph_caller() modifica la instrucción jmp en ftrace_graph_call asumiendo que está a 5 bytes cerca de jmp (e9). Sin embargo, es un jmp corto que consta de 2 bytes solamente (eb). Y ftrace_stub() se encuentra justo debajo de ftrace_graph_caller por lo que la modificación anterior rompe la instrucción que da como resultado que el kernel oops en ftrace_stub() con el código de operación no válido, como se muestra a continuación:

El parche (que se muestra a continuación) solucionó el problema del echo , pero aún no entiendo por qué el echo estaba rompiendo anteriormente cuando no lo era write() .

diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S index ed48a9f465f8..e13a695c3084 100644 --- a/arch/x86/kernel/mcount_64.S +++ b/arch/x86/kernel/mcount_64.S @@ -182,7 +182,8 @@ GLOBAL(ftrace_graph_call) jmp ftrace_stub #endif -GLOBAL(ftrace_stub) +/* This is weak to keep gas from relaxing the jumps */ +WEAK(ftrace_stub) retq END(ftrace_caller)

a través de https://lkml.org/lkml/2016/5/16/493


Parece que no eres la única persona que nota este comportamiento. Ya veo

como un informe del problema, y

como un parche para el núcleo que lo dirige. Leyendo todo ese hilo, parece que el problema son algunas optimizaciones del compilador.