Niedawno napotkałem problem w niestandardowym sterowniku jądra systemu Linux (2.6.31.5, x86), w którym copy_to_user okresowo nie kopiował żadnych bajtów do przestrzeni użytkownika. Zwróci liczbę bajtów przekazanych do niej, wskazując, że nie skopiowała niczego. Po sprawdzeniu kodu stwierdziliśmy, że kod blokował przerwania podczas wywoływania copy_to_user, co narusza jego umowę. Po rozwiązaniu tego problemu problem przestał występować. Ponieważ problem zdarzał się tak rzadko, muszę udowodnić, że wyłączenie przerw spowodowało problem.Co się dzieje, gdy instrukcja mov powoduje błąd strony z przerwaniami wyłączonymi na x86?
Jeśli spojrzysz na fragment kodu poniżej z arch/x86/lib/usercopy_32.c rep; movsl kopiuje słowa do przestrzeni użytkownika przez liczbę w CX. Rozmiar jest aktualizowany przy pomocy CX przy wyjściu. CX będzie 0, jeśli movsl wykona poprawnie. Ponieważ CX nie wynosi zero, movs? instrukcje nie mogły zostać wykonane, aby pasowały do definicji copy_to_user i obserwowanego zachowania.
/* 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)
W 2 pomysły, które mam są:
- gdy przerwania są wyłączone, usterka nie wystąpi i następnie rep; movs? jest pomijany bez robienia czegokolwiek. Wartość zwracana byłaby wówczas wartością CX lub wartością, która nie została skopiowana do przestrzeni użytkownika, zgodnie z definicją i zaobserwowanym zachowaniem.
- Błąd strony występuje, ale linux nie może go przetworzyć, ponieważ przerwania są wyłączone, więc procedura obsługi błędów strony pomija tę instrukcję, chociaż nie wiem, w jaki sposób mógłby to zrobić program obsługi błędów strony. Ponownie, w tym przypadku CX pozostałaby niezmodyfikowana, a wartość zwracana byłaby poprawna.
Czy ktoś może wskazać mi sekcje w podręcznikach Intela, które określają to zachowanie, lub wskazać mi jakieś dodatkowe źródło linuksowe, które mogłoby być pomocne?
wspominasz, że "kod blokował przerwania". Czy możesz opracować, które przerwania i jak? ... – TheCodeArtist
@ TheCodeArtist: write_lock_bh(); odbyło się, co moim zdaniem wyłącza przerywanie oprogramowania. – Edward
@ TheCodeArtist: Dzięki! Twój komentarz sprawił, że zajrzałam do write_lock_bh() znacznie bliżej, pokazując mi drogę! – Edward