Do spisu tresci tematu 7 Podprogram obslugi dysku twardego
Spis tresci
Wprowadzenie Komunikacja miedzy dyskiem twardym a komputerem Algorytm obslugi zadan do dysku (funkcja strategii) Struktury danych opisujace dysk i funkcje inicjujace Wczytywanie partycji Struktura file_operation dla dysku twardego i jej funkcje funkcje : hd_ioctl, hd_open, hd_release i struktura file_operation dla dysku
Zegar dla przerwan
Wprowadzenie
Wszystkie dane i przyklady zostaly opracowane na podstawie driveru dysku hd.c (najczesciej uzywany). Driver hd.c narzuca nastepujace warunki na dyski ktore obsluguje :
dyski zarejestrowane sa w tablicy urzadzen blokowych pod numerem 3 max liczba dyskow obslugiwanych przez driver wynosi 2 kazdy dysk moze posiadac max 64 partycje. rozmiar sektora dyku 512 bajtow. numer partycji to 6 najmlodszych bitow numeru urzadzenia 7-my wskazyje fizyczny numer dysku ( 0 lub 1 ) 8-my jest niewykorzystywany
Komunikacja miedzy dyskiem twardym a komputerem
Dysk twardy komunikuje sie z komputerem (poprzez kontroler) przy pomocy przerwan (przerwania powoduja wywolanie odpowiedniej funkcji obslugi zadania np. read_intr - czytanie danych z dysku) i przez odpowiednie porty (porty do komunikacji sa ustalane w czasie inicjowania danych opisujacych dysk,). Porty sluza do wymiany informacji miedzy dyskiem a komuteram.
Funkcje driveru informuja dysk o akcji do wykonania przy pomocy funkcji outb_p(komenda, HD_CMMAND), gdzie HD_COMMAND jest numerem portu. Czesciej uzywane komendy dla dysku : WIN_READ zadanie czytania z dysku WIN_WRITE zadanie pisania na dysk WIN_RESTOR uzywane przy rekalibrowaniu ( o tym dalej ) WIN_SETMULTM zadanie dla dysku by ustawil nowa ilosc sektorow do transmisji przy jednej operacji czytania/pisania WIN_IDENTYFI zadanie by dysk przekazal dane o sobie (ileosc sektorowna sciezce itp. uzywane wraz z funkcja identyfi_intr ktora wczytuje dane od dysku)
Przy skladaniu polecenia rozrozniane jest dla ktorego dysku ono bedzie.
Funkcje driveru moga pobierac poprzez odpowiedni port stan dysku funkcja inb_p(HD_STATUS) gdzie HD_STATUS jest numerem portu Czesciej spotykane stany dysku : ERR_STAT nastapil blad ( np proba czytania z bad sektora ) READY_STAT dysk gotowy do realizacji zadania BUSY_STAT dysk jest czyms zajety np wyszukiwaniem sektora. DRQ_STAT dysk jest w trakcie transmisji danych ( oczekuje na dane ). SEEK_STAT dysk ustawil glowice w miejscu od ktorego zacznie sie transmisja danych
Dzieki temu funkcja driveru moze ustalic kiedy dochodzi do sytuacji nieprawidlowych. W przypadku wykrycia sytuacji blednej dysk jest recalibrowany (omowienie rekalibrowania w funkcji driveru do_special_op) lub resetowany ( omowienie resetowania w funkcji driveru hd_reset, z grubsza polega to na odnowieniu danych zwiazanych z dyskami w systemie i recalibrowaniu wszystkich dyskow). Bledy dysku mozna sprawdzac funkcja inb(HD_ERROR) gdzie HD_ERROR jest numerem portu. Czesciej spotykane bledy zwracane przez dysk: BBD_ERR proba czytania z uszkodzonego sectora ECC_ERR nie poprawialny blad ID_ERR nie znaleziono bloku identyfikacyjnego dysku TRK0_ERR sciezka zero nieznaleziona
Algorytm obslugi zadan do dysku
Schemat dzialania driveru dysku w systemie
Schemat pobierania zadan do dysku Funkcja strategii zapewnia komunikacje miedzy kolejka zadan a driverem urzadzenia. Ogolnie rzecz biorac funkcja strategii pobiera pierwszwe zadanie z kolejki i inicjuje jego realizacje. Funkcja strategii wywolywana jest zawsze po nadejsciu zadania do pustej kolejki.
schemat dzialania funkcji driveru hd.c opis oznaczen na rysunku Bloki oznaczaja funkcje driveru, wychodzace z nich strzalki pokazuja, ktore funkcje sa wywolywane numery przy strzalkach pokazuja kolejnosc wywolywania funkcji wytluszczone numery ozanczaja funkcje ktore sa wywolywane zawsze , chude numery ozanczaja funkcje wywolywane opcjonalnie Funkcje driveru :
hd_out funkcja strategii hd_request do_special_op bad_rw_intr reset_hd funkcje wywolywane przez przerwania
void hd_out (int drive, int nsect, int sect, int head, int cyl, int cmd, void (*intr_addr)(void))
wejscie : - drive - fizyczny numer dysku twardego - nsect - ile sektorow nalezy wczytac/zapisac - sect, head, cyl - adres na dysku od ktorego nalewzy rozpoczac realizacje zadania - cmd - komenda dla dysku ( czytanie, pisanie ... ) - intr_addr - funkcja ktora zostanie wywolana w najblizszym przerwaniu od dysku i wymieni informacje z dyskiem.
Funkcja hd_out informuje dysk o zadaniu do wykonania i ustawia funkcje , ktora zostanie wywolana w najblizszym przerwaniu. Dysk jest urzadzeniem wolnym ustawienie glowicy nad poczatkowym sektorem zadania zajmuje troche czasu. Dlatego dysk informuje driver o gotowasci do transmisji danych przysylajac przerwanie, ktore uruchomi odpowiednia funkcje obslugi. W ten sposob czas nie jest tracony Algorytm : 1. czeka (aktywnie) az kontroler dysku bedzie gotowy jesli sie nie doczeka to nastapil jakis blad nalezy resetowc kontroler dysku (reset=1) wpp a) nalezy ustawic funkcje do wywolania przy nastepnym przerwaniu (DEVICE_INTR=intr_addr ) UWAGA: w tym miejscu jesli przerwania nie sa ignorowane moze sie zdarzyc ze przyjdzie przerwanie od dysku i wykona sie funkcja intr_addr podczas gdy nie przekazano danych dla dysku. b) zapisac informacje o zadaniu dla dysku outb_p(nsect,++port); outb_p(sect,++port); outb_p(cyl,++port); outb_p(cyl>>8,++port); outb_p(0xA0|(driveerrors >= MAX_ERRORS, MAX_ERRORS==16) lub proba transmisji danych z/do blednego setctora to usunac zadanie z kolejki (end_request(0)) i ustawic recalibrowanie dysku (special_op[dev] = 1 i rodzaj operacji recalibrate[dev] = 1) wpp jesli ilosc bledow (CURRENT->errors%RESET_FREQ==0, RESET_FREQ= 8) od ostatniego resetowania rowna jest RESET_FREQ lub wystapil blad odczytu sciezki zero to nalezy ustawic resetowanie kontrolera (reset = 1) wpp jesli ilosc bledow od ostatniego recalibrowania jest wieksza niz RECAL_FREQ (CURRENT->errors%RECAL_FREQ==0, RECAL_FREQ==4) to ustawic wykonanie specjalnych operacji (special_op[dev]=1 i rodzaj operacji recalibrate[dev]=1)
int do_special_op (unsigned int dev)
wejscie : - dev - identyfikuje fizyczny numer dysku wyjsci : wartosc zmiennej reset
Funkcja ta jest wywolywana przez funkcje strategii hd_request jesli wczesniej zostalo ustawione wywolanie operacji specjalnych (special_op[dev]=1). Funkcja do_special_op przy pomocy hd_out rejestruje wykonanie specjalnej fukcji i przekazuje komende dla dysku. Algorytm :
1. jesli recalibrate [dev] to hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr) /*rejestrowana funkcja recal_intr sprawdza status dysku (po wykonaniu przez dysk komendy WIN_RESTORE) jesli jest on nie prawidlowy to wywolywana jest funkcja bad_rw_intr ktora sprobuje cos zaradzic : usuwa zadanie ktorego nie mozna zbyt dlugo zrealizowac i ustawia recalibrowanie, ustawia samo recalibrowanie lub resetowanie. WIN_RESTORE - komenda dla dysku hd_info[dev].sect - ilosc sectorow na sciezce dev - fizyczny numer dysku */ zwracana jest wartodc zmiennej reset ktora mogla sie zmienic w funkcji hd_out 2. jesli nie ma danych o dysku (identified[dev]=0) to hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr); /*rejestrowana funkcje identify_intr wczyta dane o dysku : ilosc sektorow, glowic, cylindow , max ilosc sektorow do czytania przy jednym zadaniu (identyfi_intr jest wywolywana do przy pierwszej probie realizacji pierwszego zadania) WIN_IDENTIFY - komenda dla dysku by przekazal dane o sobie dev - fizyczny numer urzadzenia*/ zwracana jest wartodc zmiennej reset ktora mogla sie zmienic w funkcji hd_out 3. jesli (mult_req[dev] != mult_count[dev]) hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr); /*mult_req[dev] - nowa liczba transmitowanych sektorow przy jednej operacji czytania/pisania z/do dysku (wartosc mult_req[] mozna zmieniac przy pomocy funkcji hd_ioctl) mult_count[dev] - akualna liczba transmitowanych sektorow przy jednej operacji czytania/pisania z/do dysku
rejestrowana funkcja set_multmode_intr ustawi mult_count[dev]=mult_req[dev] o ile dysk przyjal wiadomosc o nowej liczbie transmitowanych sectorow przy jednej operacji czytania/pisania z/do dysku
WIN_SETMULT - komenda dla dysku by ustawil sobie nowa liczbe transmitowanych sectorow*/ zwracana jest wartodc zmiennej reset ktora mogla sie zmienic w funkcji hd_out 4. sprawdzane jest czy dysk ma wiecej niz 16 glowic jesli tak to aktualne zadanie jest usuwane ( end_request(0) ) poniewaz takie dyski nie sa obslugiwane. 5. po wykonaniu wszystkich specjalnych operacji (punkty 1, 2, 3, 4) zaznaczmy ten fakt special_op[dev]=0 i zwracamy 1.
void reset_hd(void)
Funkcja ta restartuje kontroler dyskow. Jest wywolywana przez funkcje strategii hd_request jesli wartosc globalnej zmiennej reset == 1 . Wartosc zmiennej reset moze ulec zmianie w funkcji bad_rw_intr lub hd_out w przypadku blednych stanow dysku . Algorytm : 1. jesli reset==1 to reset=0 reset_controller() i=-1 /*funkcja reset_controller() przez port HD_CMD zawiadamia dyski o resetowaniu i - zmienna statyczna wskazuje fizyczny numer dysku 0 lub 1*/ wpp a) jesli status dysku jest bledny to wywolywana jest funkcja bad_rw_intr, ktora spobuje cos zaradzic (w tym wypadku moze tylko zmienic wartosc zmiennej rest) b) jesli reset == 1 to wykonujemy funkcje reset_hd od poczatku 2. jesli (++i < NR_HD ) to /*NR_HD - rzeczywista liczba dyskow w systemie i - numer dysku ktorego dane zostana odnowione */ a) ustawic operacje specjalne dla dysku i (special_op[i]=1) rodzaj operacji calibrowanie dysku (reacalibrate[i]=1) b) przywrocic wyjsciawa liczbe transmitowanych sectorow przy jednej operacji czytania/pisania z/do dysku (mult_count[i] = 0, mult_req[i] = DEFAULT_MULT_COUN) c) hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1, hd_info[i].cyl,WIN_SPECIFY,&reset_hd) /*hd_info[i].sect - ilosc sectorow na sciezce hd_info[i].head - ilosc glowic dysku hd_info[i].cyl - ilosc cylindrow WIN_SPECIFY - komenda dla dysku reset_hd - funkcja ktora uruchomi sie w najblizszym przerwaniu(po wykonaniu przez dysk komendy WIN_SPECIFY).Wykona operacje z punktu 2 dla nastepnego dysku (i) lub jesli natrafi na bledny stan dysku proces resetowania zacznie sie od poczatku*/ d) /*funkcja hd_out mogla zmienic wartosc zmiennej reset na 1*/ jesli reset==1 to wykonac funkcje reset_hd od poczatku
wpp po zakonczeniu resetowania wywolac funkcje strategii hd_request ktora przygotuje realizacje nastepnego zadania w kolejce do dysku
fumkcja strategi dysku
void hd_request(void)
Funkcja ta pobiera kolejne zadania dla dysku i zawiadamia dysk o zadaniu (funkcja hd_out). hd_request wywolywana jest gdy przyjdzie zadanie do pustej kolejki. Dopoki kolejka zadan nie bedzie pusta funkcje konczace realizacje zadania (funkcje wywolywane w przerwaniach) beda wywolywac funkcje strategii Algorytm :
0. sprwdza czy zadanie (globalna zmienna CURRENT) jest wlasciwa (numer urzadzenia w zadaniu i blok do/z nastapi trnsmisja danych) 1. wyzerowanie zegara dla przerwan (zobacz zegar dla przerwan)
2. sprawdzane jest czy nalezy zresetowc dysk ( globalna zmienna reset) jesli tak (to znaczy ze w czasie ralizacji ostaniego zadania dysk spowodowal jakies bledy) to blokowane sa przerawania i wykonuje sie funkcja reset_hd. 3. jesli nalezy wykonac operacje specjalne (special_op[dev]==1) to wykonywana jest funkcja do_special_op 4. z zadania pobiearny jest numer sektora do czytania/pisania i ilosc sectorow do obsluzenia w tym zadaniu block = CURRENT->sector - numer sektora na dysku od ktorego nalezy rozpoczac realizacje zadania nsect = CURRENT->nr_sectors - ilosc sektorow do obsluzenia nastepnie numer sektora (block) jest zwiekszany o numer poczatku partycji ( hd[dev].start_sect ) nastepnie obliczany jest adres dyskowy : sec = block mod ilosc sektorow na sciezce track = block div ilosc sektorow na sciezce head = track mod ilosc sciezek w cylindrze cyl = track div ilosc sciezek wcylindrze 5. blokowane sa przerwania (cli) sprawdzana jest komenda w biezacym zadaniu ( CURRENT->cmd == READ lub CURRENT->cmd == WRITE ) i wykonywana jest funkcja ktora przekazuje zadanie do dysku i ustawia funkcje do wykonania w najblizszym przerwaniu. np. zadanie czytania hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr) (najpierw dysk odbierze komende np.WIN_READ nastepnie gdy bedzie gotowy do transmisji danych wysle przerwanie. Przerwanie uruchomi funkcje read_intr, ktora wczyta dane z dysku)
Funkcje wywolywane przez przerwania dysku
Funkcja ktora ma byc wykonana w najblizszym przerwaniu pamietana jest na zmiennej DEVICE_INTR. Kiedy przychodzi przerwanie od dysku wykonuje sie funkcja hd_interrupt
static void hd_interrupt() { void (*handler)(void) = DEVICE_INTR; /*pobranie funkcji do wykonania*/ DEVICE_INTR = NULL /*wyczyszczenie zmiennej*/ timer_active &= ~(1*HD_TIMER) /* zaznaczenie faktu, ze przyszlo przerwanie. zobacz zegar dla przerwan */ if (!handler) handler = unexpected_hd_interrupt; /*funkcja ktora obsluguje nieoczekiwane przerwania */ handler() sti() /*przywrocenie obslugi przerwan ktora zostala zablokowana przy wykonuwaniu handlera*/ }
Funkcje ktore moga zostac wywolane przez przerwanie :
read_intr ()
wczytuje dane z dysku
write_intr ()
przekazuje dane do dysku do zapisu lecz tylko po jednym sektorze przy jednym wywolaniu funkcji
read_intr ()
podobnie jak write_intr lecz moze przekazac max tyle sectorow przy jednym wywolaniu ile jest ustawionych dla urzadzenia dysku (mult_count[dysk]) wartosc te mozne zmieniac przy pomocy funkcji hd_ioctl
recal_intr()
funkcja ta jest uzywana przy recalibrowaniu dysku. Najpierw dysk wykonuje komende WIN_RESTORE nastepnie recal_intr sprawdza stan dysku jesli jest on nie prawidlowy to wywolywna jest funkcja bad_rw_intr ktora probuje cos zaradzic opis uzycia recal_intrw funkcji do_special_op
set_multimode_intr()
ustawia nowa liczbe sectorow transmitowanych przy jednej operacji czytania/pisania (read_intr,multwrite_intr) opis uzycia set_multimode_intr w funkcji do_special_op
indetify_intr()
pobiera dane o dysku z dysku jest wywolywana przed realizacja pierwszago zadania opis uzycia w funkcji do_special_op
Ogolny algorytm dzialania funkcji wywolywanych w przerwaniach :
I. funkcje realizujace zadania czytania, pisania. Algorytm : 1. czekaj (aktywnie) az dysk bedzie gotowy do transmisji danych (stan dysku DRQ_STAT) 2. jesli dysk jest gotowy do transmisji danych to przekazuj dane miedzy buforem zadania (CURRENT->buffer) a dyskiem (outsw(int port, char *buf, int ile_bajtow)-wysyla do dysku insw( int port, char *buf, int ile_bjatow)-pobiera z dysku) wpp jesli dysk nie jest gotowy do transmisji danych to (cos nie poszlo) nalezy wywolac funkcje bad_rw_intr do obslugi bledow dyskowych. 3. jesli sa jeszcze zadania do dysku to wywolac funkcje strategii hd_request, ktora zajmie sie realizacja zadania lub wykona operacj specjalne ustanowione prze wczesniej wywolana funkcje bad_rw_intr II. pozostale funkcje Algorytm : 1. sprawdzaja czy komenda wyslana do dysku (przez wczesniej wywolana funkcje hd_out) zostala wykonana (pobierajac stan dysku funkcja inb_p(HD_STATUS)) 2. jesli stan dysku jest prawidlowy to wykonuja operacje, ktore maja wykonac wpp nic nie rob, dysk nie wykonal komendy wyslanej przez hd_out 3. jesli sa zadania w kolejce do dysku to wywolac funkcje strategii hd_request
Struktury danych pisujacych dysk i funkcje Inicjujace
struktury danych dyskow uzywanych przez driver hd.c
Struktura do przechwywania najwazniejszych informacji o dysku.
struct hd_i_struct { unsigned int head, - liczba glowic sect, - liczba sektorow na sciezce cyl, - liczba cylindrow wpcom, lzone, ctl; };
przechowuja informacje o dysku sa incjowane w funkcji hd_geninit , ktora wczytuje dane z BIOS-u
struct hd_driveid zawiera mase informacji o dysku miedzy innymi posiada te same pola co struktura hd_i_struct. Struktura hd_driveid rozni sie tym od struktury hd_i_struct ze jest wczytywana z dysku. Tablica struct hd_driveid *hd_ident_info[MAX_HD] (MAX_HD - maksymalna liczba dyskow obslugiwanych przez driver hd.c MAX_HD=2) przechowuje rozszerzone informacje o dyskach. Inicjowanie tablicy hd_ident_info w funkcji identify_intr ktora jest wywolywana dla kazdego dysku przed realizacja pierwszego zadania.
Kazde zadanie do dysku ma liste buforow z/do ktorych nastapi wczytywanie danych z/do dysku. Wielkosci buforow w zadaniach dla kazdej partycji sa przechowywane w tablicy int hd_blocksizes[MAX_HD