linux723 74J3UUBGPQFVMGAHSFHDGW7DNDALDVJEZATRVFY




Podprogram obsulgi dysku twardego



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

recal_intr
set_mult_mode_intr
identify_intr
read_intr
write_intr





Funkcje driveru





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;
};





Tablice

struct hd_i_struct hd_info[],
struct hd_i_struct bios_info[]

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

Wyszukiwarka