Do spisu tresci tematu 7

Podprogram obslugi dysku twardego




Spis tresci


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 :

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

(rysunek)


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.

(rysunek)


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 :

(rysunek)

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|(drive<<4)|HEAD,++PORT); OUTB_P(CMD,++PORT); 

void bad_rw_intr(void)

funkcja ta jest wywolywana przez funkcje driveru uruchamiane w przerwaniach w przypadku gdy dysk znajduje sie w nieprawidlowym stanie. Funkcja bad_rw_intr sprawdza i reaguje na bledy dysku.
Algorytm :
0. zwiekszyc ilosc prob realizacji bierzacego zadania (++CURRENT->errors )
1. jesli ilosc bledow realizacji zadania wynosi MAX_ERRORS 
         (CURRENT->errors >= 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 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<<6] (MAX<<6==128) hd_blocksize[] jest inicjowana w funkcji hd_geninit. Dla kazdej partycji wielkosc bufora w zadaniu dla niej wynosi 1024 bajty.


Struktura hd_struct opisuje partycje dyskowa

struct hd_struct {
	long start_sect - sektor poczatkowy partycji
	long nr_sects   - liczba sektow w partycji
};
Informacje o partycjach dyskowych przechowywane sa w tablicy struct hd_struct hd[MAX_HD<<6] gdzie MAX_HD<<6 ="128." hd[] poprzez numer drugorzedny urzadzenia. Przypominam, ze 6 najmlodszych bitow w numerze drugorzednym to numer partycji, 7-my bit to fizyczny numer dysku (8-my bit jest nie wykorzystywany). Dzieki takiej organizacji numeru drugorzednego tablica hd[0..63] zawiera partycje dysku pierwszego, hd[64..127] zawiera partycje dysku drugiego. Tablica hd[] uzywana jest w funkcji strategii hd_request przy tworzeniu adresu dyskowego. Tablica hd[] inicjowana jest w funkcji check_partition i extended_partition. Proces wczytywania partycji dyskowych jest omowiony dalej.


Dane o typie dysku przechowywane sa w specjalnej strukturze zdefiniownej w pliku genhd.h

struct gendisk  {
      int major;         - numer glowny typu dysku
      char *major_name;  - identyfikator typu dysku
      int minor_shift;   - liczba bitow jakie nalezy obciac w numerze 
                           drugorzednym aby otrzymac fizyczny numer
                           dysku
      int max_p;         - maxymalna ilosc partycji na jednym dysku 
      int max_nr;        - maxymalna liczba dyskow obslugiwanych
                           przez driver tego typu dysku

      void (*init)(void)     - funkcja inicjujaca dane dysku
      struct hd_struct *part - tablica partycji dyskow obslugiwanych
                               przez driver
	 
      int *sizes;            - rozmiary partycji  
      int nr_real            - rzeczywista liczba dyskow w systemie 
      void *real_devices;    - wskaznik do tablicy z dnymi o dyskach
      struct gendisk *next;  - wskaznik do nastepnej struktury gendisk
}
Dla wszystkich typow dyskow w systemie tworzona jest kolejka gendisk_head struktur gendisk

Dla dyskow oblugiwanych przez driver hd.c mamy zdefiniowana nastepujaca strukture gendisk :

static struct gendisk hd_gendisk {
      MAJOR_NR,      numer glowny MAJOR_NR==3 	
      "hd",          nazwa 
      6,             nalezy obciac 6 najmlodszych bitow w numerze 
                     drugorzednym by uzyskac fizyczny numer dysku 
      1 << 2 MAX MAX_HD, DYSKOW MOZE 6, 64 ZAWIERAC HREF="#HD_GENINIT" DYSK LICZBA PARTYCJE JEDEN hd_geninit,  funkcja inicjujaca dysk.

      hd        wskaznik do tablicy partycji dyskow

      hd_sizes,      tablica rozmiarow poszczegolnych partycji
      0,             ilosc urzadzen wykrytych w systemie zostanie 
                     ustalona w funkcji inicjujacej hd_geninit 
      (void *)bios_info  informacje o dyskach (ile sectorow na sciezke itp.)
      NULL               nastepny geninit dla innych dyskow 
                         zostanie ustalony przy rejestracji urzadzenie 
};

Funkcje inicjujaca dane dysku

Funkcja inicjujaca podstawowe dane dysku
void hd_geninit(void)
Algorytm :
0. sprawdzic ile dyskow znajduje sie w systemie i zapisac w 
   hd_gendisk.nr_real
1. pobrac dane z BIOS-u o dyskach i wczytac do tablic
   hd_info[], bios_info[]
2. ustawic pierwsza partycje dla kazdego dysku.
   Pierwsza partycja obejumeje caly dysk.
3. ustawic funkcje ktora ma sie wywolac gdy przyjdzie 
   przerwanie dyskowe HD_IRQ
   request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd")
4. zarezerwowac porty do komunikacji
    request_region(HD_DATA, 8, "hd") - zwykle porty
    request_region(HD_CMD, 1, "hd(cmd)") - port uzywany do resetowania
5. ustawic rozmiar bufora w zadaniu do partycji dysku 
   (tablica hd_blocksizes[]).
   Dla kazdej partycji (1024 byte)	
Rejetracja dysku w tablicy urzadzen blokowych
unsigned long hd_init (unsigned long mem_start, unsigned long mem_end) Parametry wejsciowe mem_start i mem_end nie sa w zaden sposob wykorzystywane. Funkcja zwraca mem_start.
Alogorytm :
 1. zarejestrowac typ dysku w tablicy urzadzen blokowych
    register_blkdev(MAJOR_NR,"hd",&hd_fops)
    (MAJOR_NR - dla omawianych dyskow wynisi 3 
     hd_fops  - wskaznik do struktury file_operation dysku twardego)
 2. ustawic funkcje strategii dla dysku 
    blk_dev[MAJOR_NR].request_fn = hd_request
    (blk_dev[MAJOR_NR] jest struktura blk_dev_struct, ktora
    przachowyje kolejke zadan do urzedzen blokowych i 
    funkcje strategii realizujaca zadania)
 3. wstawic strukture hd_gendisk do kolejki gendisk_head danych o dyskach 
 4. ustawic funkcje hd_times_out by byla wywolywana w przypadku 
    gdy dysk zbyt dlugo (0.6 sek) nie przysyla przerwan.

Wczytywanie partycji dyskowych

Wstep

Kiedy system startuje najpierw wywolywana jest funkcja rejestrujaca hd_init (wywolanie w pliku main.c) nastepnie dla kazdego typu dysku (kazdej sruktury gendisk z kolejki gendisk_head) wywolywana jest funkcja setup_dev, ktora inicjuje dane dysku i wczytuje partycje z dysku.

Informacje o partycji dyskowej przechowywane sa w srtukturze partition.

struct partition {
	unsigned char head;		/* poczatkowa glowica */
	unsigned char sector;		/* poczatkowy sector */
	unsigned char cyl;		/* poczatkowy cylinder */
	unsigned char sys_ind;		/* typ partycji */ 
	unsigned char end_head;		/* koncowa glowica */
	unsigned char end_sector;	/* koncowy sector */
	unsigned char end_cyl;		/* koncowy cylinder */
	unsigned int start_sect;	/* sector poczatkowy partycji */
	unsigned int nr_sects;		/* liczba sektorow w partycji */
};
Dla kazdego dysku pierwsza partycja zawiera caly dysk. W bloku zerowym pierwszej partycji zapisane sa 4-ry struktury partition opisujace partycje glowne. Kazda partycja glowna typu extended dzieli sie na partycje logiczne, ktore nie maga sie juz dzielic. W bloku zero partycji extened zapisane sa dwie struktury partition. Pierwsza opisuje pierwsza partycje logiczna druga zawiera informacje (wskaznik) umozliwiajacy wczytanie bloku z danymi dla partycji drugiej i wskaznikiem umozliwiajacym wczytanie bloku z danymi parycji trzeciej itd...


Algorytm wczytywania partycji

void setup_dev (struct gendisk *dev)
Algorytm:
1. wywolywana jest funkcja dev->init() w przypadku omawianego
   dysku jest to funkcja hd_geninit
   (hd_init zaklada pierwsza partycje obejmujaca caly dysk)
2. dla kazdego dysku inicjowana jest tablica parycji funkcja
   check_partition

void check_partition () (struct gendisk *hd, unsigned int dev)

wejscie :
  hd - struktura gendisk opisujaca typ dysku
  dev - numer glowny  i numer drugorzedny urzadzenia
        (numer drugorzedny wskazyje fizyczny numer dysku i 
        pierwsza partycje na tym dysku, pierwsza partycja obejmuje
        caly dysk)
Funkcja check_partition wczytuje cztery glowne partycje i dla kazdej glownej partycji typu extended (partycje ktore zawieraja w sobie inne partycje) wczytywane sa partycje w niej zawarte
Algorytm :
1. z urzadzenia o numerze (glownym i drugorzednym) dev 
   wczytac blok(1kB) zerowy (funkcja bread)
   w bloku tym znajduja sie struktury partition
   opisujace partycje glowne
2. z wczytanego bloku pobrac informacje o 4 glownych partycjach
   Informacje o kazdej partycji wpisac do tablicy hd[]
   Dla kazdej glownej partycji typu extended wczytac partycje
   w niej zawarte funkcja extended_partition

void extended_partition () (struct gendisk *hd, int dev)

wejscie :
  hd - struktura gendisk opisujaca typ dysku
  dev - numer glowny  i numer drugorzedny urzadzenia 
        (numer drugorzedny wskazyje fizyczny numer dysku i 
        glowna partycje typu extened, dla ktorej nastapi wczytanie
        partycji logicznych w niej zawartych)
Funkcja extened_partition wczytuje parycje logiczne zawarte w partycji glownej. Numer partycji glownej i dysku zakodowany jest w numerze urzadzenia dev.
Algorytm :
 while (sa jeszcze partycje logiczne) {
   1. wczytac blok zerowy partycji o numerze dev
   2. z wczytanego bloku pobrac strukture partition
   3. ze struktury partition wczytac dane o partycji do tablicy 
      partycji hd[]
   4. pobrac nastepna strukture partition z wczytanego bloku
      bedzie ona wskazywac na nastepna partycje.
   5. jesli pobrana struktura patition wskazuje na nastepna partycje to
            a) ustawic tymczasowy poczatkowy sektor nastepnej partycji,
               (na podstawie wczytanej struktury partition),
               posluzy on do wczytania bloku zero z nastepnej
               partycji z wlasciwymi informacjami o tej partycji
            b) zwiekszy wartosc dev by wskazywala nastepna partycje
      wpp koniec petli
 }

Struktura file_operation dla dysku twardego i jej funkcje

Funkcja ioctl dla dysku twardego

int hd_ioctl
(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
wjscie :
  inode - wskaznik do i-wezla
  file  - wskaznik do tablicy plikow 
  cmd   - komenda
  arg   - przakazuje/pobiera parametry pracy dysku
wyjscie :
  0 w przpadku sukcesu wpp kod bledu
Komendy (cmd) i ich znaczenie :
HDIO_GETGEO
pobiera geometrie dysku sectory , sciezki , glowice i poczatkowy sektor partycji dla ktorej funkcja hd_ioctl zostala wywolana.
BLKRASET
ustawienie ile sektorow pliku nalezy czytac z wyprzedzeniem (wykorzystywane jest to przez funkcje block_write , block_read)
BLKRAGET
sprawdzenie ile sectorow mozna czytac z wyprzedzeniem
BLKGETSIZE
przekazuje rozmiar partycji dla ktorej wywolana zostala funkcja hd_ioclt
BLKFLSBUF
synchroniczne zapisanie buforow partycji dla ktorej zostala wywolana funkcja hd_ioclt
BLKRRPART
ponowne wczytanie tablicy partycji (tej opcji uzywa sie gdy dysk zostal wymieniony w czasie pracy komputera)
HDIO_SET_UNMASKINTR
sprawia ze przerwania od dysky nie sa ingnorowane w czasie wykonywania funkcji driveru dysku. (moze to byc niebezpieczne)
HDIO_GET_UNMASKINTR
przekazuje informacje czy przerwania od dysku sa ignorowane
HDIO_GET_MULTCOUNT
przekazuje wartosc ile sectorow moze byc wczytanych/zapisanych przy jednej operacji czytania/pisania z/do dysku.
HDIO_SET_MULTCOUNT
ustawia ile sektorow moze byc wczytanych/zapisanych przy jednej operacji czytania/pisania z/do dysku
HDIO_GET_IDENTITY
pobiera pelne dane o dysku z tablicy struct hd_driveid hd_ident_info[dysk]
BLKROSET
ustawia partycje tylko do czytania
BLKROGET
sprawdza czy urzadzenie jest tylko do pisania

Funkcja otwierajaca partycje dysku twardego

int hd_open struct inode * inode, struct file * filp)
wejscie :
 - inode - wskaznik do i-wezla
 - filp  - wskaznik do tablicy plikow
wyjscie :
  0 w przpadku sukcesu wpp kod bledu (proba otwierania partycji
  na nie istniejacym dysku)
Alorytm :
1. sprawdzic czy dysk nie jest zajety wczytywaniem parycji
   jesli jest zajety to czekac gdy parycje zostana wczytane 
   wykonywanie funkcji zostanie wznowione
2. zwiekszyc ilosc partycji otwartych na dysku.

Funkcja zamykajaca partycje dysku twardego

void hd_release (struct inode * inode, struct file * file)
wejscie :
 - inode - wskaznik do i-wezla
 - filp  - wskaznik do tablicy plikow
Alorytm :
1. zapisac synchronicznie :
   bloki, i-wezly, i super_blok partycji
2. zminiejszyc ilosc parycji otwartych na dysku

Struktura file_operation dla dysku twardego obslugiwanego przez driver hd.c

static struct file_operations hd_fops = {
	NULL,         /* lseek */
	block_read,   /*ogolna funkcja do czytania z urzadzen blokowych*/
	block_write,  /*ogolna funkcja do pisania na urzadzenich blokowych*/
	NULL,         /* readdir - bad */
	NULL,         /* select */
	hd_ioctl,     /*ioctl*/
	NULL,         /*mmap*/
	hd_open,      /* open */
	hd_release,   /* close*/
	block_fsync   /*ogolna funkcja zapisujaca synchronicznie
                      bloki urzadzenia blokowego*/
};

Zegar dla przerwan

Dysk jest urzadzeniem pracujacym w przerwaniach. Zatem jesli cos sie zepsuje w przesylaniu przerwan bedziemy czekac na przerwanie, ktore nigdy nie przyjdzie. System radzi sobie w ten sposob ze ma zapisane w specjalnej strukturze ile czasu uplynelo od wyslania polecenia do dysku, po jakim czasie ma zareagowac i funkcje ktora ma byc wywolana w zwiazku z nie przychodzacym przerawaniem. Czas wyslania polecenia do dysku jest zerowany w funkcji strategii (tuz przed wyslaniem polecenia do dysku) hd_request i zerowany jest po przyjsciu przerwania w funkcji hd_interrupt
void hd_times_out(void)

Funkcja wywolywana w zwiazku z nie przychodzacym przerwaniem
Algorytm :
1. zablokowac przerwania
2. ustawic resetowanie (reset=1)
3. wywolac funkcje strategii hd_request 
   , ktora sprobuje odnowic wspolprace z dyskiem

Bibliografia

  1. Pliki zrodlowe linuxa
  2. artykuly o urzadzeniach blokowych z Kernel Hacker's Guide
  3. M.Bach "Budowa Systemy operacyjnego UNIX"


autor : Mariusz Rokicki