Embedded Systems Programming Device drivers - interrupts and timers Iwona Kochańska Katedra Systemów Elektroniki Morskiej WETI PG April 7, 2019
Przerwania Przerwanie - sygnał powodujacy zmianę przepływu sterowania, niezależnie od aktualnie wykonywanego programu. Pojawienie się przerwania powoduje wstrzymanie aktualnie wykonywanego programu i wykonanie przez procesor kodu procedury obsługi przerwania (ang. interrupt handler). procedura ta wykonuje czynności zwiazane z obsługa przerwania i na końcu wydaje instrukcję powrotu z przerwania, która powoduje powrót do programu realizowanego przed przerwaniem. Przerwania a odpytywanie (polling) podczas odpytywania procesor sprawdza wszystkie dostępne urzadzenia w przypadku przerwania procesor zajmuje się sprzętem tylko wtedy, gdy sprzęt żada jakiejś usługi GUT Intel 2015/16 2/30
Przerwania Przerwanie przekazywane jest jako sygnał elektroniczny od urzadzenia połaczonego z systemem komputerowym Po otrzymaniu przerwania kontroler przerwań wysyła sygnał do procesora. Procesor wykrywa ten sygnał i przerywa wykonywanie bieżacego zadania, aby obsłużyć przerwanie. Procesor może następnie powiadomić system operacyjny, że wystapiło przerwanie, a system operacyjny może odpowiednio obsłużyć przerwanie. GUT Intel 2015/16 3/30
Przerwania Obsługa przerwań jest jednym z najbardziej wrażliwych zadań wykonywanych przez jadro i musi spełniać następujace wymagania: Urzadzenia sprzętowe generuja przerwania asynchronicznie (w odniesieniu do zegara procesora). Przerwania moga nadejść w dowolnym momencie. Przerwanie może nadejść w momencie, gdy jadro obsługuje inne przerwanie. Kod jadra może zawierać krytyczne regiony, których wykonywanie nie może być przerwane. Takie krytyczne regiony musza być jak najbardziej ograniczone. GUT Intel 2015/16 4/30
Przerwania a wyjatki Przerwanie (Interrupts): asynchroniczne, generowane przez sprzęt Wyjatki (Exceptions): synchroniczne, generowane przez procesor wyjatki występuja synchronicznie w stosunku do zegara procesora; często nazywane sa przerwami synchronicznymi wyjatki sa wytwarzane przez procesor podczas wykonywania instrukcji albo w odpowiedzi na bład programistyczny (np. dzielenie przez zero) wiele architektur procesorów obsługuje wyjatki w sposób podobny do przerwań, dlatego infrastruktura jadra do obsługi tych dwóch rodzajów zdarzeń jest podobna. Wywołania systemowe (System calls) - rodzaj wyjatkowów w architekturze x86. przerwanie programowe (software interrupt), które powoduje uruchomienie specjalnej części kodu jadra (system call handler) GUT Intel 2015/16 5/30
Przerwania maskowalne / niemaskowalne Przerwania maskowalne wszystkie przerwania (Interrupt Requests, IRQs) zgłaszane przez urzadzenia wejścia/wyjścia moga się znajdować w dwóch możliwych stanach: maskowane i niemaskowane przerwanie maskowane jest ignorowane przez CPU Przerwania niemaskowalne maksymalnie kilka krytycznych w całym systemie komputerowym sa zawsze rozpoznawane i obsługiwane przez CPU GUT Intel 2015/16 6/30
Wyjatki Falts np. Divide by zero, Page Fault, Segmentation Fault. Traps zgłoszone natychmiast po wykonaniu instrukcji powodujacej wyjatek (jak breakpoint) Aborts do zgłaszania poważnych błędów, takich jak awarie sprzętu i nieprawidłowe lub niespójne wartości w tabelach systemowych. GUT Intel 2015/16 7/30
Obsługa przerwań Każdy sterownik powinien zarejestrować w systemie operaccyjnym swoje procedury obsługi przerwań Interrupt handler lub interrupt service routine (ISR) - funkcja uruchamiana przez jadro systemu w odpowiedzi na wystapienie konkretnego przerwania każde urzadzenie generujace przerwanie ma przypisana funkcję obsługi przerwania funkcja obsługi przerwania to część sterownika urzadzenia W Linuksie procedury obsługi przerwań sa normalnymi funkcjami C, które posiadajac konkretne prototypy, dzięki czemu jadro może przekazać informacje o programie obsługi w standardowy sposób Jadro wywołuje procedury obsługi przerwań w odpowiedzi na przerwania i działa w specjalnym kontekście przerwań (kontekst atomowy). GUT Intel 2015/16 8/30
Obsługa przerwań Przerwanie może wystapić w dowolnym momencie -> obsługa przerwania może być wykonana w dowolnym momencie. Konieczne jest, aby program obsługi działał szybko, aby jak najszybciej wznowić wykonywanie przerwanego kodu. Jest to istotne z punktu widzenia: sprzętu: system operacyjny powinien obsłużyć przerwanie z jak najmniejszym opóźnieniem reszty systemu: program obsługi przerwań powinien wykonywać się w jak najkrótszym czasie. minimalne zadanie obsługi przerwań polega na potwierdzeniu odbioru przerwania przez system komputerowy. GUT Intel 2015/16 9/30
Kontekst procesu a kontekst przerywania Kontekst procesu (Process context) - kod jadra obługujacy wywołania systemowe spowodowane aplikacja użytkownika Kontekst przerwania (Interrupt context) - obsługa przerwań (asynchroniczna) Kontekst procesu nie jest zwiazany z kontekstem przerwania Kod jadra uruchomiony w kontekście procesu może zostać wywłaszczony Kontekst przerwania nie może zostań wywłaszczony GUT Intel 2015/16 10/30
Kontekst procesu a kontekst przerywania Kod wykonywany w kontekście przerwania nie może: zostać uśpiony przejać mutex wykonywać czasochłonne zadania uzyskać dostęp do pamięci wirtualnej przestrzeni użytkownika Obsługa przerwania powinna być wykonywana bardzo szybko Podczas uruchomienia obsługi przerwania inne przerwania nie sa obsługiwane (chyba że o wyższym priorytecie). Przerwanie o niższym priorytecie może zostać pominięte. Aby wyeliminować ten problem, przetwarzanie przerwań jest podzielone na dwie części: połowa górna i połowa dolna (top halves and bottom halves). GUT Intel 2015/16 11/30
Kontekst procesu a kontekst przerywania Połowa górna (Top half) funkcja obsługi przerwania uruchamiana natychmiast po wystapieniu przerwania wykonuje tylko krytyczne zadania (jak np. przyjęcie zgłoszenia o przerwaniu czy restart systemu) Połowa dolna (Bottom half) przetwarzanie danych powiadamia połowę górna kodu o nowych przerwaniach GUT Intel 2015/16 12/30
Pisanie funkcji obsługi przerwania - podstawowe zasady 1. Obsługa przerwań nie może wejść w stan uśpienia 2. Nie używaj muteksów w obsłudze przerwań. Zamiast tego używaj blokady z aktywnym oczekiwaniem (spinlock lock) 3. Funkcja obsługi przerwanianie może wymieniać danych z przestrzenia użytkownika 4. Funkcja obsługi przerwania powinna być wykonana tak szybko, jak to możliwe. Podziel implementację na połowę górna i dolna. 5. Gdy program obsługi przerwania już działa, odpowiadajace mu przerwanie musi być wyłaczone, dopóki program obsługi nie zostanie zakończony. 6. Procedury obsługi przerwań mog a zostać przerwane przez funkcje obsługi przerwań o wyższym priorytecie. GUT Intel 2015/16 13/30
Przerwania - API request_irq() - rejestracja IRQ prototyp: request_irq ( unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev_id) parametry: irq: numer IRQ handler: funkcja obsługi przerwania. Zwraca typ danych irq_handler_t. Wartość zwracana IRQ_HANDLED = przerwarzanie zakończone poprawnie. Wartość zwracana IRQ_NONE = przetarzanie zakończone błędem. flagi: 0 lub jedna z masek bitowych zdefiniowanych w linux/interrupt.h. name: nazwa urzadzenia zwiazanego z IRQ GUT Intel 2015/16 14/30
Przerwania - API Parametry request_irq - c.d. dev_id: IRQ współdzielone przez wiele urzadzeń Gdy program obsługi przerwań zostanie zwolniony, dev zapewnia unikalny plik cookie, aby umożliwić usunięcie tylko żadanego programu obsługi przerwań z linii przerwań. Bez tego parametru jadro nie wiedziałoby, który program obsługi usunać dla danej linii przerwań. NULL jeśli przerwanie nie jest współdzielone request_irq() nie może być wołana w konkteście przerwań, bo jest funkcja blokujac a! GUT Intel 2015/16 15/30
Przerwania - API Flagi w linux/interrupt.h: IRQF_DISABLED jeśli ustawiona - jadro wyłacza wszystkie przerwania podczas wykonywania tego programu obsługi przerwań większość procedur obsługi przerwań nie ustawia tej flagi, ponieważ wyłaczenie wszystkich przerwań jest zła praktyka. Jego użycie jest zarezerwowane dla przerwań wrażliwych na wydajność, które wykonuja się szybko. IRQF_SAMPLE_RANDOM określa, że przerwania generowane przez to urzadzenie powinny przyczyniać się tzw. entropii jadra (entropia jadra zapewnia prawdziwie losowe liczby pochodzace z różnych zdarzeń losowych) nie powinna być ustawiona dla urzadzen geenrujacych przerwania w sposób przewidywalny (jak zegar systemowy ) GUT Intel 2015/16 16/30
Przerwania - API Flagi w linux/interrupt.h: IRQF_TIMER - obsługa przerwania dla zegara systemowego IRQF_SHARED określa, że linia przerwania może być współdzielona przez wiele procedur obsługi przerwań każda funkcja obłsługi tego samego przerwania musi określić tę flagę GUT Intel 2015/16 17/30
Przerwania - API free_irq() - zwalnia IRQ zarejestrowane przez request_irq() prototyp: free_irq( unsigned int irq, void *dev_id) parametery: irq: numer IRQ dev_id: ostatni parametr request_irq. Jeśli podana linia przerwania nie jest współdzielona, funkcja usuwa moduł obsługi i wyłacza linię. Jeśli linia przerwania jest współdzielona, procedura obsługi zidentyfikowana przez dev_id jest usuwana, ale linia przerwania jest wyłaczona tylko wtedy, gdy ostatni program obsługi zostanie usunięty. Wywołanie free_irq () musi być wykonane z kontekstu procesu. GUT Intel 2015/16 18/30
Functions related to interrupt disable_irq(unsigned int irq) - uniemożliwia obługę IRQ enable_irq(unsigned int irq) - wznawia możliwość obsługi IRQ in_irq() - zwraca TRUE jeśli trwa obsługa przerwania in_interrupt() zwraca TRUE jeśli trwa obsługa przerwania lub procedura obsługi dolnej połowy GUT Intel 2015/16 19/30
Timers Timer - specjalny typ zegara odmierzajacy interwały czasowe Dwa typy timer-ów: stopwatch - timer który odmierza czas od zera timer - timer, który odmierza czas w dół od zadanej wartości W Linuksie jadro śledzi przepływ czasu za pomoca przerwań czasowych Przerwania czasowe sa generowane w regularnych odstępach czasowych za pomoca zegara systemowego Za każdym razem, gdy wystapi przerwanie timera, wartość wewnętrznego licznika jadra jest zwiększana Licznik jest inicjowany na 0 przy starcie systemu, więc reprezentuje liczbę taktów zegara od ostatniego rozruchu. GUT Intel 2015/16 20/30
Timers Zastosowania timer-ów: odpytywanie urzadzenia poprzez sprawdzanie jego stanu w regularnych odstępach czasu, gdy sprzęt nie może uruchomić przerwania użytkownik chce wysłać pewna wiadomość do innego urzadzenia w regularnych odstępach czasu wyślij bład, gdy w danym okresie nie doszło do pewnych działań GUT Intel 2015/16 21/30
Kernel Timer API nagłówek: <linux/timer.h> Timer opisany jest przez strukturę timer_list zdefiniowana w <linux/timer.h>: struct timer_list { /*... */ unsigned long expires ; void (* function )( unsigned long ); unsigned long data ; }; expires - czas wygaśnięcia timera (w jiffies) function() jest wołana w momencie okreslonym przez expires jiffy - zdefiniowany przez stała HZ. Różne platformy sprzętowa maja różna HZ. HZ - ile razy jiffy będzie zwiększone podczas 1 sekundy historycznie jadro Linuxa miało HZ=100 (jiffy było zwiększane raz na 10 ms) od wersji jadra 2.4: HZ = 1000 dla platformy i386 od wersji jadra 2.6.13: HZ = 250 dla i386 GUT Intel 2015/16 22/30
Kernel Timer API Uruchomienie timer-a: init_timer void fastcall init_timer ( struct timer_list * timer ); init_timer musi być wywołana przed wywołaniem każdej innej funkcji zegara wcześniej należy zdefiniować callback function w strukturze timer_list GUT Intel 2015/16 23/30
Kernel Timer API Uruchomienie timer-a: setup_timer void setup_timer ( timer, function, data ); ustawia dane w strukturze timer_list i inicjalizuje timer zalecana w większości przypadków GUT Intel 2015/16 24/30
Kernel Timer API Uruchomienie timer-a: DEFINE_TIMER DEFINE_TIMER ( _name, _function, _expires, _data ) nie wymaga struktury timer_list. Jadro samo utworzy ta strukturę o nazwie _name GUT Intel 2015/16 25/30
Kernel Timer API - start a timer add_timer - rozpoczyna działanie timer-a void add_timer ( struct timer_list * timer ); GUT Intel 2015/16 26/30
Modifying Kernel Timer s timeout mod_timer - zmienia czas timer-a int mod_timer ( struct timer_list * timer, unsigned long expires ); Zwraca 0, jeśli zmieniany timer jest nieaktywny, 1 - jeśli jest aktywny. Działanie identyczne do: del_timer ( timer ); timer - > expires = expires ; add_timer ( timer ); GUT Intel 2015/16 27/30
Kernel Timer API - zatrzymanie timer-a del_timer int del_timer ( struct timer_list * timer ); zwraca 0, jeśli timer był nieaktywny, 1 - jeśli był aktywny del_timer_sync int del_timer_sync ( struct timer_list * timer ); zatrzymuje timer, ale czeka, aż funkcja obsługi zakończy działanie zwraca 0, jeśli timer był nieaktywny, 1 - jeśli był aktywny GUT Intel 2015/16 28/30
Kernel Timer API - sprawdzenie stanu timer-a timer_pending - sprawdza, czy timer aktualnie działa int timer_pending ( const struct timer_list * timer ); Zwraca 0, jeśli timer nie działa, 1 - jeśli działa. GUT Intel 2015/16 29/30
Bibliography https://lwn.net/kernel/ldd3/ https://embetronicx.com/tutorials/linux/device-drivers/usingkernel-timer-in-linux-device-driver/ GUT Intel 2015/16 30/30