/*
 *  linux/fs/block_dev.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/* Opracowanie komentarzy w jęz. polskim Jakub Husak */

#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/locks.h>
#include <linux/fcntl.h>
#include <linux/mm.h>

#include <asm/segment.h>
#include <asm/system.h>

extern int *blk_size[];
extern int *blksize_size[];

#define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)
#define NBUF 64

/* Standardowa procedura zapisu na urządzenie blokowe
   Podajemy i-węzeł pliku specjalnego, oraz wskaźnik do pliku,
   na którym będziemy działać, bufor i liczbę bajtów*/

int block_write(struct inode * inode, struct file * filp,
        const char * buf, int count)
{
        int blocksize, blocksize_bits, i, buffercount,write_error;
        int block, blocks;
        loff_t offset;
        int chars;
        int written = 0;
        struct buffer_head * bhlist[NBUF];
        unsigned int size;
        kdev_t dev;
        struct buffer_head * bh, *bufferlist[NBUF];
        register char * p;


/* inicjalizacja - ile już zapisaliśmy */
        write_error = buffercount = 0;
/* Ustalamy nuer urządzenia, na którym będziemy pracować */
        dev = inode->i_rdev;
/* jeśli próba zapisu na urządzeniu tylko-odczyt */
        if ( is_read_only( inode->i_rdev ))
                return -EPERM; /* błąd dostępu */
/* standardowo BLOCK_SIZE jest zdef. w fs.h na 1024 */
        blocksize = BLOCK_SIZE;
/* Jeśli ustalone jest to, co w warunku poniżej (tzn. rozmiar bloku <> 1024) */
        if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
/* to blocksize będzie się temu równało (tzn <> 1024 ) */
                blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
/* teraz policzymy ilość bitów na blok (blok jest zawsze wielokrotn. 2)
   tzn. blocksize_bits=lg2(blocksize)
*/

        i = blocksize;
        blocksize_bits = 0;
        while(i != 1) {
                blocksize_bits++;
                i >>= 1;
        }
/* następnie ustalamy blok jako część całk. z dzielenia */
        block = filp->f_pos >> blocksize_bits;
/* i offset jako modulo długość bloku */
        offset = filp->f_pos & (blocksize-1);
/* następnie badamy rozmiar urządzenia */
        if (blk_size[MAJOR(dev)])
/* jeśli zdefiniowany, to będzie w size */
                size = ((loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
        else
/* jeśli nie, to nie mamy ograniczeń */
                size = INT_MAX;

/* Teraz główna pętla zapisu */
        while (count>0) {
/* dopóki są jeszcze bajty to zapisu */
                if (block >= size)
                        return written ? written : -ENOSPC;
/* obliczamy długość następnej porcji do zapisu */
                chars = blocksize - offset;
                if (chars > count)
/* jeśli za dużo - to obcinamy; reszta będzie zapisana w następnym obiegu */
                        chars=count;

#if 0 /* Faza rozwoju - na pewno zniknie to w przyszłych wersjach block_dev.c */
                /* get the buffer head */
                {/* a skąd inąd ciekawe rozwiązanie */
                        struct buffer_head * (*fn)(kdev_t, int, int) = getblk;
                        if (chars != blocksize)
                                fn = bread;
                 /* oszczędzamy jedno wywołanie funkcji */
                        bh = fn(dev, block, blocksize);
                }
#else
/* pobieramy nowy bufor na dane */
                bh = getblk(dev, block, blocksize);
/* Jeśli trzeba zapisać mniej niż blocksize i bufor niezapisany, to */
                if (chars != blocksize && !buffer_uptodate(bh)) {
   /* jeśli nie czytamy naprzód */
                  if(!filp->f_reada ||
                     !read_ahead[MAJOR(dev)]) {
                    /* We do this to force the read of a single buffer */
     /* Robimy to, by wymusić czytanie pojedynczego bufora */
     /* pozbywamy się pobranego bufora; niepotrzebny, bo */
                    brelse(bh);
     /* a będziemy zmieniać (zapisywać) tylko część bufora, to musimy
      odczytać bufor z urządzenia, by nie zamazać reszty danych */
                    bh = bread(dev,block,blocksize);
                  } else {
                    /* Read-ahead before write */
   /* A jeśli czytamy naprzód, to */ 
                    blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2;
   /* jeśli nie ma miejsca na dysku, to zapiszemy w tym rzucie tyle, */
   /* ile się jeszcze zmieści (a coś się jeszcze zmieści, bo byśmy tu nie doszli) */
                    if (block + blocks > size) blocks = size - block;
      /* jeśli mamy zamiar zapisać więcej, niż zmieści się w kolejce żądań */
                    if (blocks > NBUF) blocks=NBUF;
                    bhlist[0] = bh;
                    for(i=1; i<blocks; i++){
      /* pobieramy kolejno bufory */
                      bhlist[i] = getblk (dev, block+i, blocksize);
      /* jeśli się skończą, to */
                      if(!bhlist[i]){
      /* pozbywamy się wszystkich dopiero co pobranych (unikanie zakleszczenia)  */
                      while(i >= 0) brelse(bhlist[i--]);
      /* i zwracamy ilość dotychczas zapisanych bajtów, lub błąd EIO */
                        return written ? written : -EIO;
                      };
                    };

/* Aż w końcu żądamy zapisu wszelkich buforów */
                    ll_rw_block(READ, blocks, bhlist);
/* następnie wszystkich się pozbywamy */
                    for(i=1; i<blocks; i++) brelse(bhlist[i]);
/* i czekamy na zakończenie operacji */
                    wait_on_buffer(bh);
                      
                  };
                };
#endif
/* No a teraz, po zakończeniu zapisu zwiększamy blok*/
                block++;
                if (!bh)
/* Jeśli jakiś błąd, to zwracamy to, co przeczytaliśmy, lub błąd,
   jeśli nic nie przeczytaliśmy */
                        return written ? written : -EIO;
/* niech p będzie 
                p = offset + bh->b_data;
                offset = 0;
/* przesuwamy pozycje w pliku i written o tyle, ile zapisaliśmy */
                filp->f_pos += chars;
                written += chars;
/* zostało o tyleż mniej */
                count -= chars;
/* do p kopiujemy chars kolejnych bajtów z buf */
                memcpy_fromfs(p,buf,chars);
/* zwiększając odpowiednie wskaźniki */
                p += chars;
                buf += chars;
/* odhaczamy bufory, które nie są już potrzebne */
                mark_buffer_uptodate(bh, 1);
                mark_buffer_dirty(bh, 0);

                if (filp->f_flags & O_SYNC)
                        bufferlist[buffercount++] = bh;
                else
                        brelse(bh);
/* jeśli osiągnęliśmy koniec tablicy buforów, to */
                if (buffercount == NBUF){
/* żądamy zapisu tejże tablicy */
                        ll_rw_block(WRITE, buffercount, bufferlist);
                        for(i=0; i<buffercount; i++){
/* następnie po kolei czekamy na zwolnienie buforów */
                                wait_on_buffer(bufferlist[i]);
/* jeśli bufor się nie zaktualizował, to się nie lub źle zapisał - błąd */
                                if (!buffer_uptodate(bufferlist[i]))
                                        write_error=1;
/* i tak - i tak go zwalniamy */
                                brelse(bufferlist[i]);
                        }
                        buffercount=0;
                }
/* jeśli jakiś błąd, to */
                if(write_error)
                        break;
       }
/* KONIEC głównej pętli zapisu */
/* jesli coś zostało wpisane to tablicy bufferlist */
        if ( buffercount ){
                ll_rw_block(WRITE, buffercount, bufferlist);
/* to zapisujemy */
                for(i=0; i<buffercount; i++){
/* czekamy na zakończenie operacji we/wy */
                        wait_on_buffer(bufferlist[i]);
/* jeśli po zakończeniu ww. operacji bufor niezaktualizowany, to błąd */
                       if (!buffer_uptodate(bufferlist[i]))
                                write_error=1;
/* tak - czy inaczej - zwalniamy bufor */
                        brelse(bufferlist[i]);
                }
        }               
/* ustawiamy znacznik odczytu naprzód na 1 */
        filp->f_reada = 1;
/* jeśli po drodze wystąpił błąd, to teraz go zwracamy   */
        if(write_error)
                return -EIO;
/* zwracamy ilosc zapisanych bajtów - wszystkie znajdują się w buf */
        return written;
}
/* Standardowa procedura zapisu na urządzenie blokowe
*/

/* funkcja block_read czyta z urządzenia zadaną liczbę bajtów:
   struct inode * inode - i-węzeł urządzenia (pliku specjalnego)
   struct file * filp - wskaźnik do struktury pliku, na którym będziemy działać
   char * buf - bufor , do którego będziemy czytać
   int count - liczba bajtów do przeczytania
*/

int block_read(struct inode * inode, struct file * filp,
        char * buf, int count)
{
        unsigned int block;
        loff_t offset;
        int blocksize;
        int blocksize_bits, i;
        unsigned int blocks, rblocks, left;
        int bhrequest, uptodate;
        struct buffer_head ** bhb, ** bhe;
        struct buffer_head * buflist[NBUF];
        struct buffer_head * bhreq[NBUF];
        unsigned int chars;
        loff_t size;
        kdev_t dev; 
        int read;

/* Ustalamy nuer urządzenia, na którym będziemy pracować */
        dev = inode->i_rdev;

/* standardowo BLOCK_SIZE jest zdef. w fs.h na 1024 */
        blocksize = BLOCK_SIZE;
/* Jeśli ustalone jest to, co w warunku poniżej */
        if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
/* to blocksize będzie się temu równało 
   blocksize to logiczny rozmiar bloku - jeśli niezdefiniowany,
   to domyślnie 1024 bajty*/
                blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
/* teraz policzymy ilość bitów na blok (blok jest zawsze wielokrotn. 2)
   tzn. blocksize_bits=lg2(blocksize)
*/
        i = blocksize;
        blocksize_bits = 0;
        while (i != 1) {
                blocksize_bits++;
                i >>= 1;
        }

/* jaką mamy pozycję w pliku? */
        offset = filp->f_pos;
        if (blk_size[MAJOR(dev)])
/* jeśli zdefiniowana jest wielkoßć urzĆdzenia, to size będzie jej równe */
                size = (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS;
        else
/* w przeciwnym wypadku urządzenie ma nieskończoność bajtów */
                size = INT_MAX;

/* jeśli przekroczyliśmy wielkość urządzenia */
        if (offset > size)
/* to zostało do odczytania 0 bajtów */
                left = 0;
        /* size - offset might not fit into left, so check explicitly. */
        else if (size - offset > INT_MAX)
/* zostało więcej do odczytania, niż INT_MAX, na razie INT_MAX */
                left = INT_MAX;
        else
/* zostało rozmiar - offset */
                left = size - offset;
        if (left > count)
                left = count;
        if (left <= 0)
/* jeśli zostało mniej niż zero, to koniec odczytu */
                return 0;

/* liczba przeczytanych bajtów = 0 */
        read = 0;
/* a teraz liczymy numer bloku od początku pliku (dzielimy offset/rozmiar bloku)*/
        block = offset >> blocksize_bits;
/* offset = offset modulo blocksize */
        offset &= blocksize-1;
/* size = size / rozmiar bloku */
        size >>= blocksize_bits;
/* ile bloków zostało? */
        rblocks = blocks = (left + offset + blocksize - 1) >> blocksize_bits;
/* inicjalizacja zmiennych bhb i bhe na tablicę buflist[64] */
        bhb = bhe = buflist;
/* jeśli czytamy z wyprzedzeniem, to: */
        if (filp->f_reada) {
                if (blocks < read_ahead[MAJOR(dev)] / (blocksize >> 9))
                        blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9);
                if (rblocks > blocks)
                        blocks = rblocks;
                
        }
        if (block + blocks > size)
                blocks = size - block;

        /* We do this in a two stage process.  We first try to request
           as many blocks as we can, then we wait for the first one to
           complete, and then we try to wrap up as many as are actually
           done.  This routine is rather generic, in that it can be used
           in a filesystem by substituting the appropriate function in
           for getblk.

           This routine is optimized to make maximum use of the various
           buffers and caches. */

        /* Robimy to dwuetapowo. Najpierw próbujemy zażądać tak dużo bloków,
           ile możemy - wrzucamy je do tablicy hdreq; następnie czekamy, aż
           przyjdzie pierwszy blok; następnie wszystkie dotychczasowe żądania
           próbujemy zadowolić (ll_rw_block). W poniższym kodzie nie ma nic
           specjalnego, jedynie optymalizacja zaciemnia sprawę.

           Jak widać powyższy algorytm i poniższe jego wcielenie są
           zoptymalizowane ze względu na operacje na buforach
        */

        do {
                bhrequest = 0;
                uptodate = 1;
/* dopóki są bloki do odczytania *
                while (blocks) {
/* to jest ich o jeden mniej */
                        --blocks;
/* przydzielamy bufory na blok */
                        *bhb = getblk(dev, block++, blocksize);
/* a jeśli otrzymaliśmy prawidłowy wskażnik, to
   dodajemy do tablicy buforów
*/
                        if (*bhb && !buffer_uptodate(*bhb)) {
                                uptodate = 0;
                                bhreq[bhrequest++] = *bhb;
                        }
/* przechodzimy na następny element tablicy cyklicznej o rozm. NBUF=64*/
                        if (++bhb == &buflist[NBUF])
                                bhb = buflist;

                        /* If the block we have on hand is uptodate, go ahead
                           and complete processing. */
/* jeśli blok jest uaktualniony - koniec pętli */
                        if (uptodate)
                                break;
/* jeśli końcem dogoniliśmy początek, to koniec pętli*/
                        if (bhb == bhe)
                                break;
                }

                /* Now request them all */
/* teraz żądamy odczytu wszystkich nagromadzonych bloków */
                if (bhrequest) {
                        ll_rw_block(READ, bhrequest, bhreq);
                }
/* po tym wszystkim kończymy wszystkie operacje we/wy, które się skończyły */
                do { /* Finish off all I/O that has actually completed */
                        if (*bhe) {
                /* jeśli nie NULL, to czekamy na wczytanie bufora */
                                wait_on_buffer(*bhe);
                /* jeśli bufor nie został uaktualniony, to wystąpić musiał jakiś błąd */ 
                                if (!buffer_uptodate(*bhe)) {   /* read error? */
                /* zwalniamy zajęty bufor */
                                        brelse(*bhe);
                /* przejście do nast. elementu w buflist */
                                        if (++bhe == &buflist[NBUF])
                                          bhe = buflist;
                /* zostało 0 do odczytania */
                                        left = 0;
                /* koniec pętli */
                                        break;
                                }
                        }                       
                        if (left < blocksize - offset)
                                chars = left;
                        else
                                chars = blocksize - offset;
/* odczytano chars znaków, przeto f_pos przesuwamy o tyle do przodu */
                        filp->f_pos += chars;
/* o tyle mniej zostało do przeczytania */
                        left -= chars;
/* o tyle więcej przeczytano */
                        read += chars;
/* jeśli dobry bufor, to przepisz do bufora buf dane spod offset+(*bhe)->b_data */
                        if (*bhe) {
                                memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
/* zwolnij bufor */
                                brelse(*bhe);
/* zwiększ nasz wskaźnik, gdzie będziemy kopiować następne dane */
                                buf += chars;
                        } else {
/* wypełniamy nasz bufor zerami (w ilości chars) */
                                while (chars-- > 0)
                                        put_user(0,buf++);
                        }
                        offset = 0;
/* bierzemy pod lupę następny bufor z buflisty */
                        if (++bhe == &buflist[NBUF])
                                bhe = buflist;
/* dopóki coś zostało, nie dogoniliśmy końcem początku, i (koniec coś wskazuje (nie NULL)
   lub bufor *bhe zabezpieczony) */
                } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
/* dopóki coś zostało */
        } while (left > 0);

/* Release the read-ahead blocks */
/* zwalniamy wszystkie bloki przeczytane z wyprzedzeniem */
        while (bhe != bhb) {
                brelse(*bhe);
                if (++bhe == &buflist[NBUF])
                        bhe = buflist;
        };
/* jeśli nic nie przeczytaliśmy, to błąd */
        if (!read)
                return -EIO;
        filp->f_reada = 1;
/* zwrot liczby przeczytanych bajtów */
        return read;
}

int block_fsync(struct inode *inode, struct file *filp)
{/* synchronizacja urządzenia blokowego - zapisanie na dysk wszelkich
    niezapisanych buforów
*/
        return fsync_dev (inode->i_rdev);
}