# Lista 5 ###### tags: `sju21` `ćwiczenia` ## Deklaracje Gotowość rozwiązania zadania należy wyrazić poprzez postawienie X w odpowiedniej kolumnie! Jeśli pożądasz zreferować dane zadanie w trakcie dyskusji (co najwyżej jedno!) oznacz je znakiem ==X== na żółtym tle. **UWAGA: Tabelkę wolno edytować tylko wtedy, gdy jest na zielonym tle!** :::danger | | 5-1 | 5-2 | 5-3 | 5-4 | 5-5 | | ---------------------:| --- | --- | --- | --- | --- | | Jacek Bizub | X | | X | X | X | | Wojciech Jasiński | X | X | X | | X | | Artur Juraszek | | | | | | | Michał Myczkowski | | | | | | | Michał Odorczuk | X | | X | X | | | Damian Ratajski | | | | | | | Łukasz Siudek | | | | | | | Błażej Sowa | | | | | | | Andrzej Turko | X | X | X | X | X | | Maksymilian Zawartko |==X==| X | X | X | X | ::: ## Zadanie 5-1 :::info Autor: Maksymilian Zawartko ::: Wątki przerwań to wątki jądra, które uruchamiają ciąg ISRów (handlerów) wyzwolone przerwaniem sprzętowym lub programowym. Każda ISR ma nazwę, powiązaną funkcję, argument, priorytet i różne flagi. Każdy wątek przerwań ma listę ISRów posortowaną wg priorytetu, dzięki czemu ISRy o wyższym priorytecie są wykonywane przed tymi o niższym. Każdy wątek przyjmuje za swój priorytet najwyższy priorytet ISRa lub PRIO_MAX, jeśli nie ma ISRów. Wątki przerwań są powiązane z pojedynczym źródłem przerwań, reprezentowanym przez liczbę (vector number). FreeBSD Device Drivers, 126, Interrupt Handlers in FreeBSD: > In FreeBSD, interrupt handlers are composed of a filter routine, an ithread routine, or both. ISRy składają się z filtra (filter routine), ithreada (ithread routine), lub obu. Filtr nie może zmieniać kontekstu, tylko operuje w tym przerwania. Nie może też zatem się blokować, a jedyne dostępne dla niego mechanizmy synchronizacji to spinlocki (spin mutexes). Z tych właśnie powodów filtry są zwykle używane z urządzeniami, które wymagają niewywyłaszczalnych ISRów. Filtr może albo całkowicie obsłużyć przerwanie, albo przenieść odpowiedzialność za wykonanie ciężkiej obliczeniowo pracy na powiązanego ithreada, jeśli taki istnieje. Wartości zwracane przez filtr: FILTER_STRAY - oznacza, że filtr nie mógł obsłużyć przerwania; błąd. FILTER_HANDLED - przerwanie zostało całkowicie obsłużone. FILTER_SCHEDULE_THREAD - przerwania zostało przekazane do ithreada. ithread operuje w swoim własnym kontekście. W ithreadzie można robić wszystko poza dobrowolną zmianą kontekstu (spaniem) i czekaniem na zmienną warunkową. Większość ISRów we FreeBSD to ithread, bo są wywłaszczalne. pre_ithread jest wołana przed wykonaniem ithreada bez filtrów. Robi 2 rzeczy. 1. Upewnia się, że nie nastąpi burza przerwań (interrupt storm) ze źródła powiązanego z ithreadem, zwykle przez wyłączenie tych przerwań level-triggered do końca wykonania ithreada. 2. Upewnia się, że CPU może odbierać przerwania z innych źródeł. On some platforms this is done by acknowledging the interrupts via an EOI. post_ithread jest wołana po wykonaniu ithreada. Upewnia się, że CPU znów może przyjmować przerwania ze źródła wyciszonego (wyłączonego) przez pre_ithread. Zwykle implementacja obejmuje włączenie przerwań level-triggered wyłączonych przez pre_ithread. post_filter jest wołana podczas wykonania filtra (nie po - tak wynika z komentarza w `interrupt.h`). Upewnia się, że CPU znów może przyjmować przerwania. On some platforms this is done by acknowledging the interrupts via an EOI. ## Zadanie 5-2 :::info Autor: Wojciech Jasiński ::: * jakie niepożądane zjawiska mogłyby wystąpić, gdybyśmy wewnątrz procedur `ih_filter` i `ih_handler` przeszli odpowiednio w sen ograniczony i sen nieograniczony? - sen ograniczony w `ih_filter` - czekamy aż pewien wątek zwolni blokadę, na którą czekamy - jego priorytet jest podbijany przez turnstile, więc nie jest beznadziejnie - procedury obsługi przerwań `ih_handler` się nie wykonują (niższy priorytet) - skoro zmieniamy kontekst na inny wątek, to przywracamy jego maskę przerwań i znowu możemy je otrzymywać - w przypadku przerwań wyzwalanych poziomem wprowadzi nas to w pętlę obsługi przerwań (ISR -> filter -> sleep -> zmień kontekst -> ISR) - fatalny przypadek: wątek, na który czekamy, czeka na wątek, w którego kontekście obsługujemy przerwanie -> deadlock - sen nieograniczony w `ih_handler` - przerwanie jest w tym czasie wyłączone - nie wiemy jak długo będziemy spać, więc nie wiemy, jak dużo przerwań stracimy - całkiem możliwe, że urządzenie nie dostanie ważnej odpowiedzi w czasie i przestanie działać ## Zadanie 5-3 :::success Autor: Michał Odorczuk ::: > The functions tsleep(), [...] handle event-based thread blocking. > If priority includes the PCATCH flag, pending signals are allowed to interrupt the sleep, otherwise pending signals are ignored during the sleep. > > Because it is complex to handle signals in the midst of doing some other operation, many sleep requests are not interruptible; that is, a thread will not be scheduled to run until the event for which it is waiting occurs. For example, a thread waiting for disk I/O will sleep with signals blocked. [...] Occasionally, an event that is supposed to occur quickly, such as a disk I/O, will get held up because of a hardware failure. Because the thread is sleeping in the kernel with signals blocked, it will be impervious to any attempts to send it a signal, even a signal that should cause it to exit unconditionally. The only solution to this problem is to change sleep()s on hardware events that may hang to be interruptible. In the remainder of this book, we shall always use sleep() when referring to the routine that puts a thread to sleep, even when one of the mtx_sleep(), sx_sleep(), rw_sleep(), or t_sleep() interfaces is the one that is being used. 1) Oczekujemy na stronę z pamięci - dysk ulega awarii - zdarzenie jest ignorowane - sen nie zostaje przerwany i wątek śni snem wiecznym 2) Oczekujemy na inny obiekt w systemie - dysk ulega awarii - zdarzenie jest ignorowane - jesteśmy wybudzani ale nie jesteśmy świadomi jego awarii Proces może wysypać się ze względu na opuszczone komunikaty lub wręcz nigdy się nie wybudzić. :::spoiler 1. Dysk może ulec awarii. W takim wypadku wątek, który czekał na zdarzenie związane z dyskiem (I/O), nigdy się nie obudzi, nawet po wysłaniu do niego sygnału. 2. Sygnały mogą się kumulować, może ich być bardzo dużo, co ma negatywny wpływ na zajmowaną przez nie pamięć i późniejszy czas ich obsługi. ::: ## Zadanie 5-4 :::info Autor: Jacek Bizub ::: > W [1,§3.4] opisano procedurę `hardclock`. W nowszych wersja jądra FreeBSD jej funkcje przejęła procedura [handleevents](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_clocksource.c?r=80f39bd9#handleevents), która jest wołana w kontekście przerwania sprzętowego z procedury [hardclockintr](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_clocksource.c?r=80f39bd9#hardclockintr). Ograniczając się do procedur z modułu [kern_clocksource.c](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_clocksource.c?r=80f39bd9) przeprowadź uczestnikówzajęć przez procedurę [handleevents](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_clocksource.c?r=80f39bd9#handleevents) oraz opisz zadania jakie realizuje. Zakładamy, że parametr `fake` jest równy `0`. Opisz zawartość struktury [cpu_state](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_clocksource.c#pcpu_state). ```c= 113 struct pcpu_state { 114 struct mtx et_hw_mtx; /* Per-CPU timer mutex. */ 115 u_int action; /* Reconfiguration requests. */ 116 u_int handle; /* Immediate handle resuests. */ 117 sbintime_t now; /* Last tick time. */ 118 sbintime_t nextevent; /* Next scheduled event on this CPU. */ 119 sbintime_t nexttick; /* Next timer tick time. */ 120 sbintime_t nexthard; /* Next hardclock() event. */ 121 sbintime_t nextstat; /* Next statclock() event. */ 122 sbintime_t nextprof; /* Next profclock() event. */ 123 sbintime_t nextcall; /* Next callout event. */ 124 sbintime_t nextcallopt; /* Next optional callout event. */ 125 int ipi; /* This CPU needs IPI. */ 126 int idle; /* This CPU is in idle mode. */ 127 }; ``` ------ Obsługa `hardclock` musi być jak najszybsza. Nie chcemy zgubić żadnych ticków czy pakietów sieciowych. Dlatego główna procedura zajmuje się obsługą krytycznych rzeczy (jak nastawienie czasu systemowego i timerów). Mniej ważne rzeczy są delegowane do wątku o mniejszym priorytecie (`softclock`). Aby nie wykonywać za często obsługi przerwania zegarowego, nie wykonujemy go standardowo 1000 razy na sekundę a zamiast tego wyliczamy kiedy nastąpi następny event wymagający obsługi i nastawiamy zegar żeby wtedy nas obudził. Jeżeli system nie posiada oddzielnych zegarów do zbierania statystyk czy profilowania to hardclock wykonuje ich rolę. ## Zadanie 5-5 :::info Autor: Andrzej Turko ::: **userret()** jest wołana po to, aby z przestrzeni jądra wrócić do wykonywania kodu użytkownika (np. po wykonaniu pułapki). Podczas tej procedury sygnały są dostarczane do zadania. Jeśli jest to konieczne, znajdujemy nowe zadanie schedulerem i wracamy do niego. [mi_userret](https://mimiker.ii.uni.wroc.pl/source/xref/NetBSD/sys/sys/userret.h?r=1fe5c4da#mi_userret) 1. Upewniamy się, że nikt nie trzyma kernel locka 2. Jeśli na tym cpu potrzebny jest rescheduling, wywłaszczamy obecne zadanie 3. Jeśli są odpowiednie flagi, wołamy lwp_userret 4. Uaktualniamy priorytet obecnego zadania (ustawiamy na nasz) [cpu_need_resched](https://mimiker.ii.uni.wroc.pl/source/xref/NetBSD/sys/arch/riscv/riscv/riscv_machdep.c?r=1fe5c4da#cpu_need_resched) 1. Jeżeli możemy wywłaszczyć zadanie od razu: wysyłamy IPI (wywłaszczenie) na inne cpu, a jeśli mamy zrobić reschedule lokalnie, powodujemy odpowiednie przerwanie softwareowe. 2. Jeśli rescheduling ma się wydarzyć na innym cpu, generujemy na nim przerwanie. 3. Jeśli chcemy wywołać rescheduling normalnie, requestujemy o wywołanie AST (Asynchronous Software Trap). AST wywołuje mi_userret, które zajmie się reschedulingiem. [lwp_userret](https://mimiker.ii.uni.wroc.pl/source/xref/NetBSD/sys/kern/kern_lwp.c?r=1fe5c4da#lwp_userret) 1. Przetwarzamy sygnały. 2. W przypadku core-dump albo suspension -- zawieszamy nasze wykonywanie, zapisujem (mi_switch zmienia kontekst) 3. W zależności od flag, kończymy zadanie. 4. Uaktualniamy dane zadania. [preempt](https://mimiker.ii.uni.wroc.pl/source/xref/NetBSD/sys/kern/kern_synch.c?r=1fe5c4da#preempt) 1. Zwalniamy kernel lock 2. Jeśli zostaliśmy oznaczeni jako CPU hogger, to zdejmujemy podwyższenie priorytetu (kernel priority boost) 3. Wołamy mi_switch -- zmiana kontekstu.