FUNKCJE:

getblk()
find_buffer()
get_hash_table()



// **********  find_buffer()   **********

// Funkcja find_buffer szuka bufora na liście haszującej, przeglądając
// elementy jeden za drugim. Zwraca wskaźnik do nagłówka, gdy znajdzie
// bufor, NULL w przeciwnym przypadku

static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
{
        struct buffer_head * tmp;

        // Funkcja hash(dev, block) zwraca wskaźnik do pierwszego elementu
        // odpowiedniej kolejki haszującej (czyli odpowiadającej
        // wartościom dev i block)

        // W pętli poniżej przechodzimy więc elementy jeden za drugim
        // na liście i patrzymy, czy zgadzają się wartości b_dev,
        // b_blocknr i b_size nagłówka bufora z podanymi z zewnątrz

        for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
                if (tmp->b_blocknr == block && tmp->b_dev == dev)
                        if (tmp->b_size == size)
                                return tmp;
                        else {
                                printk("VFS: Wrong blocksize on device %s\n",
                                        kdevname(dev));
                                return NULL;
                        }
        return NULL;
}

/*
 * Why like this, I hear you say... The reason is race-conditions.
 * As we don't lock buffers (unless we are reading them, that is),
 * something might happen to it while we sleep (ie a read-error
 * will force it bad). This shouldn't really happen currently, but
 * the code is ready.
 */



//  **********   get_hash_table()   **********

// Funkcja get_hash_table szuka bufora na liście haszującej.
// Jeśli nie znajdzie, zwraca NULL
// Jeśli znajdzie, czeka, aż będzie on dostępny (locked<>0), następnie
// zwraca wskaźnik do nagłówka tego bufora.
// Czekanie wiąże się z uśnięciem procesu, więc dopuszczeniem
// innych procesów do działania.

struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
{
        struct buffer_head * bh;

        for (;;) {
                if (!(bh=find_buffer(dev,block,size)))
                        return NULL;
                // find_buffer znajduje bufor na liście haszującej


                // teraz trzeba zaczekać na bufor aż będzie dostępny:

                bh->b_count++;
                // Zwiększamy o jeden wskaźnik użytkowania bufora,
                // żeby nie mógł zostać zwolniony.

                wait_on_buffer(bh);
                // Jeśli bufor jest niedostępny (locked), w funkcji
                // wait_on_buffer ,,uśniemy'' czekając na niego

                if (bh->b_dev == dev && bh->b_blocknr == block
                                             && bh->b_size == size)
                        return bh;
                // Ponowne sprawdzenie, czy jest to bufor odpowiadający
                // podanemu blokowi dyskowemu. Ponowne, ponieważ wcześniej
                // sprawdziła to już funkcja find_buffer(). Jednakże bufor
                // mógł zostać zmieniony, np. przez procedurę obsługi
                // przerwania w sytuacji, gdy nastąpił błąd dyskowy.

                bh->b_count--;
                // Wskaźnik b_count był zwiększony tylko tymczasowo na
                // potrzeby tej funkcji.
        }
};



//  **********   getblk()   *********

/*
 * Ok, this is getblk, and it isn't very clear, again to hinder
 * race-conditions. Most of the code is seldom used, (ie repeating),
 * so it should be much more efficient than it looks.
 *
 * The algorithm is changed: hopefully better, and an elusive bug removed.
 *
 * 14.02.92: changed it to sync dirty buffers a bit: better performance
 * when the filesystem starts to get full of dirty blocks (I hope).
 */


//  Zadaniem funkcji getblk jest uzyskanie bufora dla podanego bloku
//  dyskowego. Jeśli bufora odpowiadającego podanemu blokowi nie ma jeszcze
//  w pamięci buforowej, to przydzielany jest dla niego nowy bufor.
//  Funkcja zwraca wskaźnik do nagłówka bufora.

struct buffer_head * getblk(kdev_t dev, int block, int size)
{
        struct buffer_head * bh;
        int isize = BUFSIZE_INDEX(size);

        // isize jest skrótem od index_size.
        // Po wykonaniu powyższej instrukcji isize zawiera
        // numer listy, na której są wolne bufory podanego rozmiaru.
        // Dokładnie: dla size=512 isize=0, dla size=1024 isize=1 itd.


        /* If there are too many dirty buffers, we wake up the update process
           now so as to ensure that there are still clean buffers available
           for user processes to use (and dirty) */
repeat:
        allow_interrupts();
        // Funkcja allow_interrupts() ma zastosowanie wyłącznie przy
        // architekturach wieloprocesorowych i pozwala na przerwanie
        // pracy w trybie jądra jednemu procesorowi przez drugi -
        // w naszym modelu nie ma ona zastosowania i można ją pominąć.


        bh = get_hash_table(dev, block, size);
        // Wpierw próbujemy znaleźć bufor w odpowiedniej liście
        // haszującej - robi to funkcja get_hash_table(). Jeśli
        // bufor zostanie znaleziony, ale będzie niedostępny (locked),
        // to proces będzie musiał na niego zaczekać (zostanie ,,uśpiony''),
        // ale w końcu go dostanie.
        // Jeśli get_hash_table() zwróci NULL, to znaczy, że bufora nie
        // ma jeszcze w pamięci buforowej.

        if (bh) {
                // Jeśli bufor został znaleziony:

                if (!buffer_dirty(bh)) {
                        // Jeśli nie jest ,,brudny'' (dirty), czyli
                        // nie jest przeznaczony do zapisu:

                        if (buffer_uptodate(bh)) put_last_lru(bh);
                        // Zgodnie z algorytmem LRU, umieszczamy bufor
                        // na końcu odpowiedniej kolejki lru.

                        bh->b_flushtime = 0;
                        // (b_flushtime==0) oznacza, że bufora nie trzeba
                        // zapisywać.

                }
                set_bit(BH_Touched, &bh->b_state);
                // Zaznaczamy bufor jako ,,dotknięty'' (touched).
                // To pole nagłówka ma znaczenie przy zwalnianiu
                // stron pamięci zajmowanych przez bufory.

                return bh;
        }

get_free:
        // Trzeba przydzielić nowy bufor:

        bh = free_list[isize];
        // Patrzymy na pierwszy bufor na liście wolnych odpowiedniego
        // rozmiaru

        if (!bh) goto refill;
        // Jeśli nie ma wolnego bufora odpowiedniego typu, skaczemy
        // do "refill", czyli do wywołania funkcji refill_freelist().
        // Powinna ona stworzyć wolne bufory.

        remove_from_free_list(bh);
        // Usuwa z listy wolnych.

        /* OK, FINALLY we know that this buffer is the only one of its kind,
         * and that it's unused (b_count=0), unlocked (buffer_locked=0),
         * and clean */

        // Mamy nowy bufor, który nie jest przez nikogo
        // używany i nie jest zablokowany.
        // Teraz trzeba ustawić wszystkie niezbędne wartości
        // nagłówka bufora:
        bh->b_count=1;              // Użytkowany przez jeden proces.
        bh->b_list=BUF_CLEAN;       // Będzie na liście ,,czystych'' (CLEAN).
        bh->b_flushtime=0;          // Nie jest przeznaczony do zapisu.
        bh->b_state=(1<<b_dev=dev;  // Pola b_dev i b_blocknr będą
        bh->b_blocknr=block;        // jednoznacznie identyfikować bufor.
        insert_into_queues(bh);     // Umieszczamy na odpowiedniej liście
                                    // lru i odpowiedniej liście haszującej
        return bh;

refill:
        // jeśli nie ma wolnych buforów odpowiedniego rozmiaru, to
        // spróbujemy pozyskać wolne bufory (funkcja refill_freelist):
        allow_interrupts();
        refill_freelist(size);        // wstawia bufory do listy wolnych

        if (!find_buffer(dev,block,size)) goto get_free;
        // Funkcja find_buffer szuka bufora w odpowiedniej liście
        // haszującej. Mógł on się tam pojawić, pomimo że wcześniej go
        // nie znaleźliśmy, gdyż w funkcji refill_freelist dopuszczamy
        // możliwość ,,zaśnięcia'' procesu i inny proces mógł wstawić
        // poszukiwany przez nas bufor.
        // Jeśli jednak wciąż go tam nie ma, to skaczemy do get_free,
        // gdzie już z pewnością zostanie przydzielony bufor z listy
        // wolnych.

        goto repeat;
        // Jeśli inny proces w miedzyczasie wstawił bufor, to
        // wracamy do początku, gdzie teraz na pewno funkcja
        // get_hash_table() zwróci poszukiwany bufor.
};



Autor komentarza: Tomasz Bogusławski