Recientemente encontré un problema en un controlador kernel Linux personalizado (2.6.31.5, x86) donde copy_to_user periódicamente no copiaba ningún byte en el espacio del usuario. Devolvería el recuento de bytes pasados, lo que indica que no había copiado nada. Después de la inspección del código, descubrimos que el código estaba deshabilitando las interrupciones al llamar a copy_to_user, que infringe su contrato. Después de corregir esto, el problema dejó de ocurrir. Debido a que el problema sucedió con poca frecuencia, necesito demostrar que la desactivación de las interrupciones causó el problema.¿Qué sucede cuando una instrucción mov causa un error de página con interrupciones deshabilitadas en x86?
Si mira el siguiente fragmento de código de arch/x86/lib/usercopy_32.c rep; movsl copia las palabras en el espacio de usuario según el conteo en CX. El tamaño se actualiza con CX en la salida. CX será 0 si el movsl se ejecuta correctamente. Debido a que CX no es cero, ¿los movs? las instrucciones no deben haberse ejecutado, para ajustarse a la definición de copy_to_user y al comportamiento observado.
/* Generic arbitrary sized copy. */
#define __copy_user(to, from, size) \
do { \
int __d0, __d1, __d2; \
__asm__ __volatile__( \
" cmp $7,%0\n" \
" jbe 1f\n" \
" movl %1,%0\n" \
" negl %0\n" \
" andl $7,%0\n" \
" subl %0,%3\n" \
"4: rep; movsb\n" \
" movl %3,%0\n" \
" shrl $2,%0\n" \
" andl $3,%3\n" \
" .align 2,0x90\n" \
"0: rep; movsl\n" \
" movl %3,%0\n" \
"1: rep; movsb\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"5: addl %3,%0\n" \
" jmp 2b\n" \
"3: lea 0(%3,%0,4),%0\n" \
" jmp 2b\n" \
".previous\n" \
".section __ex_table,\"a\"\n" \
" .align 4\n" \
" .long 4b,5b\n" \
" .long 0b,3b\n" \
" .long 1b,2b\n" \
".previous" \
: "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) \
: "3"(size), "0"(size), "1"(to), "2"(from) \
: "memory"); \
} while (0)
Los 2 ideas que tengo son:
- cuando las interrupciones están deshabilitadas, el error de página no se produce y luego representante; Movs? se omite sin hacer nada. El valor de retorno sería CX, o la cantidad no copiada en el espacio de usuario, como especifica la definición y el comportamiento observado.
- Se produce el error de página, pero Linux no puede procesarlo porque las interrupciones están deshabilitadas, por lo que el controlador de fallas de página omite las instrucciones, aunque no sé cómo lo haría el controlador de fallas de página. De nuevo, en este caso CX permanecería sin modificaciones y el valor de retorno sería correcto.
¿Alguien me puede indicar las secciones de los manuales de Intel que especifican este comportamiento o indicarme alguna fuente adicional de Linux que pueda ser útil?
mencionas que "el código estaba deshabilitando las interrupciones". ¿Puede explicar qué interrupciones y cómo? ... – TheCodeArtist
@TheCodeArtist: write_lock_bh(); se llevó a cabo, lo cual, según mi entender, deshabilita las interrupciones de software. – Edward
@TheCodeArtist: ¡Gracias! ¡Tu comentario me hizo mirar en write_lock_bh() mucho más de cerca, mostrándome el camino! – Edward