do spisu tresci tematu 7

7.2.1 Urzadzenia blokowe - wprowadzenie


Spis tresci


Struktury danych

Kazde urzadzenie blokowe opisane jest przez strukture blk_dev_struct zdefiniowana w pliku naglowkowym blkdev.h


struct blk_dev_struct {
	void (*request_fn)(void);
	struct request *current_request;
	struct request plug;
	struct tq_struct plug_tq;
}

request_fn
wskaznik na procedure strategii (strategy routine) czyli najwazniejsza czesc programu obslugi urzadzenia blokowego, zajmujaca sie szeregowaniem zadan
current_request
kolejka struktur opisujacych zadania wejscia-wyjscia skierowane do urzadzenia
plug
specjalne, nieaktywne zadanie sluzace tylko do "zatkania" kolejki, wskaznik current_request wskazuje na nie, kiedy kolejka jest pusta i wlasnie dokladamy do niej nowe zadania,
plug_tq
struktura typu task queue, (patrz kolejki zadan), zawiera funkcje usuwajaca strukture plug z kolejki zadan,

Struktury blk_dev_struct sa elementami tablicy blk_dev o rozmiarze MAX_BLKDEV, czyli 64 w wersji Linuxa 2.0.0.

Pozostale struktury danych (plik blkdev.h ) to:

int * blk_size[MAX_BLKDEV]
indeksowana numerami glownymi urzadzen tablica, ktorej elementami sa indeksowane numerami drugorzednymi tablice zawierajace ilosc blokow, ktore zawiera urzadzenie,
int * blksize_size[MAX_BLKDEV]
Podobna do blk_size struktura (tablica dwuwymiarowa), zawierajaca wielkosc jednego bloku urzadzenia, jesli wartosc blksize_size[nr_glowny] nie jest zdefiniowana (NULL) to przyjmuje sie wielkosc 1024 bajtow
int * hardsect_size[MAX_BLKDEV]
dwuwymiarowa tablica zawierajaca wielkosci sektorow, zalezne od fizycznej budowy urzadzenia; domyslna wielkoscia sektora jest 512 bajtow
int read_ahead[MAX_BLKDEV]
ilosc sektorow, ktore maja zostac przeczytane przy operacji czytania z wyprzedzeniem,
int ro_bits[MAX_BLKDEV][8]
flagi, ktorych ustawienie oznacza, ze urzadzenie jest tylko do odczytu;

Operacje wejscia-wyjscia na urzadzeniach blokowych

Programy obslugi urzadzen blokowych nie implementuja wlasnych funkcji read ani write. Zamiast tego uzywaja wspolnych dla wszystkich urzadzen blokowych funkcji block_read i block_write. Funkcje te korzystaja z mechanizmu podrecznej pamieci buforowej, wiec w ich lepszym zrozumieniu moze pomoc lektura poswieconego buforom tematu 5 (szczegolnie opis funkcji getblk, wait_on_buffer, brelse ).

Algorytm block_read (z pliku fs/block_dev.c)


DEFINICJA: 
long block_read( struct inode *inode,	/* i-wezel pliku specjalnego   	*/
		struct file *filp,	/* opis pliku specjalnego
				           w tablicy plikow 		*/
		char *buf,		/* bufor w przestreni uzytkownika
				           na odczytane dane     	*/
		unsigned long count )	/* liczba bajtow do przeczytania */
WYNIK: ilosc przeczytanych bajtow lub kod bledu EIO

{
    bhreq - tablica wskaznikow na naglowki buforow;
    zadeklaruj licznik przeczytanych bajtow;
    pobierz z i-wezla numer urzadzenia;
    ustal wielkosc bloku, pozycje w pliku i numer pierwszego bloku;
    ustal liczbe blokow do odczytania; 
    
    do {
	
	oproznij tablice bhreq;
	aktualny = 1;
        /* 
	  zazadaj przydzielenia wolnych buforow, jesli 
	  pierwszy z nich bedzie zawieral aktualne dane 
          to wyskocz z petli i przepisz je
	*/
	while (sa bloki do odczytania) 
	{
	    zmniejsz liczbe blokow do odczytania;
	    /* zazadaj przydzielenia buforow na blok :*/
	    getblk(numer urzadzenia, numer bloku, wielkosc bloku);
	    dodaj bufor do tablicy bhreq;
	    if (zadany bufor nia zawiera aktualnych danych)
		aktualny = 0;    		
	    if (aktualny = 1)
		break;
	}
    	
	/* zazadaj wczytania danych do przydzielonych 
	   buforow z tablicy bhreq:	
	*/
	ll_rw_block(READ,liczba buforow do wczytania, bhreq);
	do {
	    ustaw wskaznik bhe na poczatek tablicy bhreq;
	    /* czekaj na wczytanie bufora */
	    wait_on_buffer ( bhe );
	    /* sprawdz, czy nie ma bledu */
	    if ( bhe nie zawiera aktualnych danych )
	    {
	        zwolnij bufor bhe;
		liczba bajtow do przeczytania = 0;
		break;
	    }
	    przepisz dane z bufora do przestrzeni uzytkownika;
	    zwolnij bufor;
	    zwieksz licznik przeczytanych bajtow;    	
	    przesun wskaznik bhe;
	} while (bhe nie wskazuje na NULL)
   
    } while (liczba bajtow do przeczytania > 0)
    
    zwolnij pozostale przydzielone bufory;
    /* wywolujac dla kazdego brelse(bufor) */
    if (liczba przeczytanych bajtow = 0)
	return -EIO;    
    return ( liczba przeczytanych bajtow );
}

Komentarz:

Algorytm block_write (z pliku fs/block_dev.c)


DEFINICJA:
long block_write( struct inode *inode,	/* i-wezel czytanego pliku	*/
		struct file *filp,	/* struktura w tablicy plikow	*/
		const char *buf,	/* bufor z danymi do zapisania	*/
		unsigned long count)	/* ilosc bajtow do zapisania	*/
WYNIK: ilosc zapisanych bajtow lub kod bledu EIO;

{
    bhlist - tablica przydzielonych buforow;
    if( urzadzenie tylko do odczytu )
	return -EPERM;
    ustal wielkosc bloku, pozycje w pliku i ilosc blokow do zapisania;
    
    while( bajtow do przeczytania > 0 ) 
    {
        if( numer zapisywanego bloku > wielkosc urzadzenia w blokach )
	    return( liczba bajtow zapisanych lub -ENOSPC jesli rowna 0);
	/* zazadaj przydzialu wolnego bufora */
        bufor = getblk( nr urzadzenia, nr bloku, wielkosc bloku );
 
	if( aktualny blok jest ostatni do zapisania )
	{
	    zazadaj przydzialu buforow na bloki
	    przeczytane z wyprzedzeniem (getblk) i
	    umiesc bufory w tablicy bhlist;
	    przeczytaj bloki z wyprzedzeniem ( ll_rw_block );
	    if( blad czytania )
		return -EIO
	    zwolnij wszystkie oprocz pierwszego (brelse);
	}
 	zmniejsz ilosc bajtow do czytania;
	kopiuj dane do bufora;
	zaznacz bufor jako aktualne (mark_buffer_uptodate); 
	if( uzylismy juz 64 buforow (wielkosc tablicy bhlist))
        {
	    zazadaj zapisania buforow z bh_list (ll_rw_block);
	    czekaj na zapisanie buforow (wait_on_buffer);
	}
    }        
    zazadaj zapisania buforow z bh_list (ll_rw_block);
    czekaj na zapisanie;
    if( wystapil blad )
	return -EIO;
     return( ilosc przeczytanych bajtow );
}

Jak widac, ani block_read ani block_write nic bezposrednio nie zapisuja ani nie czytaja, a tylko zlecaja odpowiednie operacje funkcji ll_rw_block, o ktorej w rozdziale o szeregowaniu zadan.


Zrodla informacji

  1. Pliki z kodem zrodlowym Linuxa 2.0.0:
  2. Michael K. johnson: Kernel Hackers' Guide - artykul o urzadzeniach blokowych
  3. Michael K. Johnson: Writing Linux Device Drivers


Artur Zawlocki