/*
 *  linux/drivers/block/ll_rw_blk.c
 *
 * Copyright (C) 1991, 1992 Linus Torvalds
 * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics
 */

/*
 * This handles all read/write requests to block devices
 */
// ----------------------------------------------------------------------- //
// ------- komentarze opracował Marcin Chałotowski --------- //
// ------------------------------------------------------------------------//

// generalnie znajdujące się tu funkcje dotyczą kolejek żądań, dokładniej 
// ich tworzenia. Stąd też jest wywoływana procedura strategii dla urządzenia,
// jeśli właśnie dodano żądanie do pustej listy żądań
// Krótki opis funkcji w tym pliku:
//- ll_rw_block jest to funkcja która jest wywoływana z buffer.c 
// ustanawia ona żądanie odczytu z dysku pewnej iloąci sektorów. Wywołuje ona funkcję:
//- make_request, która jest odpowiedzialna za stworzenie żądania (get_request) i wywołanie 
//- add_request - funkcji ustawiającej żadanie w odpowiednim miejscu kolejki żądań (algorytm windy)
//- get_request, get_request_wait, __get_request_wait - zajmują wolne miejsce w tablicy żądań, w zależności od
//  sytuacji dwie ostatnie czekają na wolne miejsca.
// - ll_rw_swap_file - specjalna funkcja do ustanawiania żądań dotyczących pliku wymiany

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/config.h>
#include <linux/locks.h>
#include <linux/mm.h>

#include <asm/system.h>
#include <asm/io.h>
#include <linux/blk.h>

/*
 * The request-struct contains all necessary data
 * to load a nr of sectors into memory
 */
static struct request all_requests[NR_REQUEST];

/*
 * The "disk" task queue is used to start the actual requests
 * after a plug
 */
DECLARE_TASK_QUEUE(tq_disk);

/*
 * used to wait on when there are no free requests
 */
struct wait_queue * wait_for_request = NULL;

/* This specifies how many sectors to read ahead on the disk.  */

int read_ahead[MAX_BLKDEV] = {0, };

/* blk_dev_struct is:
 *      *request_fn
 *      *current_request
 */
struct blk_dev_struct blk_dev[MAX_BLKDEV]; /* initialized by blk_dev_init() */

/*
 * blk_size contains the size of all block-devices in units of 1024 byte
 * sectors:
 *
 * blk_size[MAJOR][MINOR]
 *
 * if (!blk_size[MAJOR]) then no minor size checking is done.
 */
int * blk_size[MAX_BLKDEV] = { NULL, NULL, };

/*
 * blksize_size contains the size of all block-devices:
 *
 * blksize_size[MAJOR][MINOR]
 *
 * if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
 */
int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };

/*
 * hardsect_size contains the size of the hardware sector of a device.
 *
 * hardsect_size[MAJOR][MINOR]
 *
 * if (!hardsect_size[MAJOR])
 *              then 512 bytes is assumed.
 * else
 *              sector_size is hardsect_size[MAJOR][MINOR]
 * This is currently set by some scsi device and read by the msdos fs driver
 * This might be a some uses later.
 */
int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };

/*
 * remove the plug and let it rip..
 */
void unplug_device(void * data)
{
        struct blk_dev_struct * dev = (struct blk_dev_struct *) data;
        unsigned long flags;

        save_flags(flags);
        cli();
        if (dev->current_request == &dev->plug) {
                struct request * next = dev->plug.next;
                dev->current_request = next;
                if (next) {
                        dev->plug.next = NULL;
                        (dev->request_fn)();
                }
        }
        restore_flags(flags);
}

/*
 * "plug" the device if there are no outstanding requests: this will
 * force the transfer to start only after we have put all the requests
 * on the list.
 *
 * This is called with interrupts off and no requests on the queue.
 */
static inline void plug_device(struct blk_dev_struct * dev)
{
//zadanie blokujące kolejkę zadań
        dev->current_request = &dev->plug;
        queue_task_irq_off(&dev->plug_tq, &tq_disk);
}

/*
 * look for a free request in the first N entries.
 * NOTE: interrupts must be disabled on the way in, and will still
 *       be disabled on the way out.
 */
static inline struct request * get_request(int n, kdev_t dev)
{
// UWAGA!!   Funkcja get_request(...) jest statyczna,
// podobnie zmienne prev_found i prev_limit
        static struct request *prev_found = NULL, *prev_limit = NULL;
        register struct request *req, *limit;

// kontrola parametru funkcji
        if (n <= 0)
                panic("get_request(%d): impossible!\n", n);

// ostatnie dopuszczalne żądanie 
        limit = all_requests + n; 
// jeśli limit żądań zmienił się od ostatniego razu to zaczniemy szukać
// od początku wpp startujemy od końca ostatniego poszukiwania
        if (limit != prev_limit) {
                prev_limit = limit;
                prev_found = all_requests;
        }
// znajdź nieaktywne żądanie, czyli miejsce w tablicy aal_requests
// robione jest to od końca czyli od limit-1 do all_requests
        req = prev_found;
        for (;;) {
                req = ((req > all_requests) ? req : limit) - 1;
                if (req->rq_status == RQ_INACTIVE)
                        break;
                if (req == prev_found)
                        return NULL;
        }
// uaktualnij ostatnio znalezione
        prev_found = req; 
// wpisz odpowiednie dane do żądania 
// (nie wszystkie reszta jest wpisywana w make_request(...)
        req->rq_status = RQ_ACTIVE;
        req->rq_dev = dev;
        return req;
}

/*
 * wait until a free request in the first N entries is available.
 */
static struct request * __get_request_wait(int n, kdev_t dev)
// STATYCZNA !!!! wykonując tą funkcję wiemy już, 
// że nie ma wolnych żądań
{
        register struct request *req;
        struct wait_queue wait = { current, NULL };

// włóż się do kolejki czekających 
// na wolne żądania (=miejsca w tablicy all_requests)
        add_wait_queue(&wait_for_request, &wait);
        for (;;) {
                current->state = TASK_UNINTERRUPTIBLE;
                cli();
// zajmij żądanie
                req = get_request(n, dev);
                sti();
// jeśli zajął to O.K. - wychodzimy
                if (req)
                        break;
// jeśli nie to czekaj dalej
                run_task_queue(&tq_disk);
                schedule();
        }
// usuń swoje dane z kolejki czekających
        remove_wait_queue(&wait_for_request, &wait);
        current->state = TASK_RUNNING;
        return req;
}

static inline struct request * get_request_wait(int n, kdev_t dev)
// STATYCZNE!!! To samo co powyższa funkcja tylko
// sprawdzamy najpierw czy nie ma wolnych żądań
{
        register struct request *req;

        cli();
        req = get_request(n, dev);
        sti();
        if (req)
                return req;
        return __get_request_wait(n, dev);
}

/* RO fail safe mechanism */

static long ro_bits[MAX_BLKDEV][8];

int is_read_only(kdev_t dev)
// sprawdzenie czy urządzenie jest read-only
{
        int minor,major;

        major = MAJOR(dev);
        minor = MINOR(dev);
        if (major < 0 || major >= MAX_BLKDEV) return 0;
        return ro_bits[major][minor >> 5] & (1 << (minor & 31));
}

void set_device_ro(kdev_t dev,int flag)
// ustawienie urządzenia jako read-only
{
        int minor,major;

        major = MAJOR(dev);
        minor = MINOR(dev);
        if (major < 0 || major >= MAX_BLKDEV) return;
        if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
        else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
}

static inline void drive_stat_acct(int cmd, unsigned long nr_sectors,
                                   short disk_index)
// funkcja uaktualniająca statystyki jądra
{
        kstat.dk_drive[disk_index]++;
        if (cmd == READ) {
                kstat.dk_drive_rio[disk_index]++;
                kstat.dk_drive_rblk[disk_index] += nr_sectors;
        } else if (cmd == WRITE) {
                kstat.dk_drive_wio[disk_index]++;
                kstat.dk_drive_wblk[disk_index] += nr_sectors;
        } else
                printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
}

/*
 * add-request adds a request to the linked list.
 * It disables interrupts so that it can muck with the
 * request-lists in peace.
 *
 * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
 * which is important for drive_stat_acct() above.
 */

void add_request(struct blk_dev_struct * dev, struct request * req)
{
        struct request * tmp;
        short            disk_index;
// uaktualnij statystyki dotyczące operacji dyskowych
        switch (MAJOR(req->rq_dev)) {
                case SCSI_DISK_MAJOR:
                        disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4;
                        if (disk_index < 4)
                                drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
                        break;
                case IDE0_MAJOR:        /* same as HD_MAJOR */
                case XT_DISK_MAJOR:
                        disk_index = (MINOR(req->rq_dev) & 0x0040) >> 6;
                        drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
                        break;
                case IDE1_MAJOR:
                        disk_index = ((MINOR(req->rq_dev) & 0x0040) >> 6) + 2;
                        drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
                default:
                        break;
        }

        req->next = NULL;
// będziemy modyfikować kolejkę żądań, 
// niezbędne jest wyłączenie obsługi przerwań
        cli();
// zaznacz bufor jako czysty
        if (req->bh)
                mark_buffer_clean(req->bh);
// jeśli kolejka zadań jest pusta to dodaj nasze zadanie
// i wywołaj procedurę strategii
// każde kolejne żądanie dodawane do kolejki żądań
// jest obsługiwane przez procedurę startegii wywołaną 
// przez funkcję end_request(...) 
        if (!(tmp = dev->current_request)) {
                dev->current_request = req;
                (dev->request_fn)();
// włšcz z powrotem przerwania i wyjdź z funkcji
                sti();
                return;
        }
// !!! algorytm windy !!!!!!!
// IN_ORDER sprawdza czy numery sektorów są  w kolejności
        for ( ; tmp->next ; tmp = tmp->next) {
                if ((IN_ORDER(tmp,req) ||
                    !IN_ORDER(tmp,tmp->next)) &&
                    IN_ORDER(req,tmp->next))
                        break;
        }
        req->next = tmp->next;
        tmp->next = req;

/* for SCSI devices, call request_fn unconditionally */
        if (scsi_blk_major(MAJOR(req->rq_dev)))
                (dev->request_fn)();

        sti();
}

static void make_request(int major,int rw, struct buffer_head * bh)
{
        unsigned int sector, count;
        struct request * req;
        int rw_ahead, max_req;

        count = bh->b_size >> 9;
        sector = bh->b_rsector;

        /* Uhhuh.. Nasty dead-lock possible here.. */
// nie możemy nic robić na zablokowanym buforze
        if (buffer_locked(bh))
                return;
        /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */

// zajmij bufor
        lock_buffer(bh);

// sprawdzenie czy dobrze podano parametry 
// tzn. czy jest taki sektor na dysku
        if (blk_size[major])
                if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
                        bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
                        /* This may well happen - the kernel calls bread()
                           without checking the size of the device, e.g.,
                           when mounting a device. */
                        printk(KERN_INFO
                               "attempt to access beyond end of device\n");
                        printk(KERN_INFO "%s: rw=%d, want=%d, limit=%d\n",
                               kdevname(bh->b_rdev), rw,
                               (sector + count)>>1,
                               blk_size[major][MINOR(bh->b_rdev)]);
                        unlock_buffer(bh);
                        return;
                }

        rw_ahead = 0;   /* normal case; gets changed below for READA/WRITEA */
        switch (rw) {
                // żądanie read-ahead (czytanie naprzód)
                case READA:
                        rw_ahead = 1;
                        rw = READ;      /* drop into READ */
                case READ:
                        if (buffer_uptodate(bh)) { // czyli bh->b_state == BH_Uptodate
                                unlock_buffer(bh); /* Hmmph! Already have it */
                                return;
                        }
                        kstat.pgpgin++;
                        max_req = NR_REQUEST;   /* reads take precedence */
                        break;
                case WRITEA:
                        // write-ahead
                        rw_ahead = 1;
                        rw = WRITE;     /* drop into WRITE */
                case WRITE:
                        // jeśli został już zapisany - wyjdź (bh->b_state==BH_Dirty;)
                        if (!buffer_dirty(bh)) {
                                unlock_buffer(bh); /* Hmmph! Nothing to write */
                                return;
                        }
                        /* We don't allow the write-requests to fill up the
                         * queue completely:  we want some room for reads,
                         * as they take precedence. The last third of the
                         * requests are only for reads.
                         */
                        kstat.pgpgout++;
                        // 1/3 kolejki żądań jest tylko dla READ
                        max_req = (NR_REQUEST * 2) / 3;
                        break;
                default:
                        printk(KERN_ERR "make_request: bad block dev cmd,"
                               " must be R/W/RA/WA\n");
                        unlock_buffer(bh);
                        return;
        }

/* look for a free request. */
       /* Loop uses two requests, 1 for loop and 1 for the real device.
        * Cut max_req in half to avoid running out and deadlocking. */
// " Loopback devices are used to mount filesystems not associated 
//   with block devices. This binding to the loopback devices is usually
//   handled by mount"
        if (major == LOOP_MAJOR)
             max_req >>= 1;

        /*
         * Try to coalesce the new request with old requests
         */
        cli();
// kolejka żądań dla danego urządzenia
        req = blk_dev[major].current_request;
        if (!req) {
                /* MD and loop can't handle plugging without deadlocking */
                // MD_MAJOR=9 "metadisk (RAID) devices"
                // zatkaj kolejkę żeby nie zablokować urządzenia
                if (major != MD_MAJOR && major != LOOP_MAJOR)
                        plug_device(blk_dev + major);
        } else switch (major) {
             case IDE0_MAJOR:   /* same as HD_MAJOR */
             case IDE1_MAJOR:
             case FLOPPY_MAJOR:
             case IDE2_MAJOR:
             case IDE3_MAJOR:
                /*
                 * The scsi disk and cdrom drivers completely remove the request
                 * from the queue when they start processing an entry.  For this
                 * reason it is safe to continue to add links to the top entry for
                 * those devices.
                 *
                 * All other drivers need to jump over the first entry, as that
                 * entry may be busy being processed and we thus can't change it.
                 */
                req = req->next;
                if (!req)
                        break;
                /* fall through */

             case SCSI_DISK_MAJOR:
             case SCSI_CDROM_MAJOR:

        // obejrzyj kolejkę żądań dla urządzenia sprawdzając
        // czy bieżącego żądania nie można przyczepić do już 
        // istniejącego, jeśli tak to potem szybciej się wykona (dwa w jednym!)
                do {
                // sprawdź warunki konieczne do majstrowania przy żądaniu
                        if (req->sem)
                                continue;
                        if (req->cmd != rw)
                                continue;
                        if (req->nr_sectors >= 244)
                                continue;
                        if (req->rq_dev != bh->b_rdev)
                                continue;
                        /* Can we add it to the end of this request? */
                        if (req->sector + req->nr_sectors == sector) {
                                req->bhtail->b_reqnext = bh;
                                req->bhtail = bh;
                        /* or to the beginning? */
                        } else if (req->sector - count == sector) {
                                bh->b_reqnext = req->bh;
                                req->bh = bh;
                                req->buffer = bh->b_data;
                                req->current_nr_sectors = count;
                                req->sector = sector;
                        } else
                                continue;

                        req->nr_sectors += count;
                        mark_buffer_clean(bh);
                        sti();
                        return;
                } while ((req = req->next) != NULL);
        }
// niestety dołączenie się nie powiodło utwórz żądanie -
// - potrzebne miejsce w tablicy all_requests
/* find an unused request. */
        req = get_request(max_req, bh->b_rdev);
        sti();

/* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
// gdy jest to read-ahead to nie jest niezbędne do dalszej pracy - wyjdź
// wpp. Wywołaj funkcję, która zaczeka na wolne miejsce w all_requests
        if (!req) {
                if (rw_ahead) {
                // trzeba pamiętać o odblokowaniu bufora
                        unlock_buffer(bh);
                        return;
                }
                req = __get_request_wait(max_req, bh->b_rdev);
        }

/* fill up the request-info, and add it to the queue */
// dokończ wypełnianie pól żądania (zaczęto to robić w get_request(...) )
// typ operacji dyskowej
        req->cmd = rw;
// liczba błędów jakie wystąpiły
        req->errors = 0;
// miejsce na dysku
        req->sector = sector;
// ilość sektorów do odczytania
        req->nr_sectors = count;
// ile jeszcze zostało do odczytania
        req->current_nr_sectors = count;
// bufor
        req->buffer = bh->b_data;
// semafor
        req->sem = NULL;
// wskaźniki do bufora w którym mają się znaleźć dane (bądź już tam są)
        req->bh = bh;
        req->bhtail = bh;
        req->next = NULL;
// włóż żądanie w odpowiednie miejsce w kolejce żądań (algorytm windy)
        add_request(major+blk_dev,req);
}

/* This function can be used to request a number of buffers from a block
   device. Currently the only restriction is that all buffers must belong to
   the same device */

void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
// nr - liczba buforów
// bh[] - tablica wskaźników na nagłówki buforów
// funkcja która jest wywoływana z poza tego pliku (dokładniej - z buffer.c)
{
        unsigned int major;
        int correct_size;
        struct blk_dev_struct * dev;
        int i;

        /* Make sure that the first block contains something reasonable */
// ustawienie bh na pierwszy dobry nagłówek bufora
// (jednocześnie nr maleje)
        while (!*bh) {
                bh++;
                if (--nr <= 0)
                        return;
        }

        dev = NULL;
// odczytanie z tablicy rozdzielczej urządzenia
        if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
                dev = blk_dev + major;
// sprawdzenie porpawności wpisu w tablicy rozdzielczej
// (czy urządzenie istnieje i czy ma procedurę startegii)
        if (!dev || !dev->request_fn) {
                printk(KERN_ERR
        "ll_rw_block: Trying to read nonexistent block-device %s (%ld)\n",
                kdevname(bh[0]->b_dev), bh[0]->b_blocknr);
                goto sorry;
        }

        /* Determine correct block size for this device.  */
        // długość bloku dla tego urządzenia
        correct_size = BLOCK_SIZE;
        if (blksize_size[major]) {
                i = blksize_size[major][MINOR(bh[0]->b_dev)];
                if (i)
                        correct_size = i;
        }

        /* Verify requested block sizes.  */
        // kontroluj poprawność danych w nagłówkach buforów z długością bloku
        for (i = 0; i < nr; i++) {
                if (bh[i] && bh[i]->b_size != correct_size) {
                        printk(KERN_NOTICE "ll_rw_block: device %s: "
                               "only %d-char blocks implemented (%lu)\n",
                               kdevname(bh[0]->b_dev),
                               correct_size, bh[i]->b_size);
                        goto sorry;
                }

                /* Md remaps blocks now */
                // policzenie fizycznego położenia na dysku
                bh[i]->b_rdev = bh[i]->b_dev;
                bh[i]->b_rsector=bh[i]->b_blocknr*(bh[i]->b_size >> 9);
#ifdef CONFIG_BLK_DEV_MD
                // dla RAID policz 'specjalnie' położenie fizyczne danych do odczytu/zapisu
                if (major==MD_MAJOR &&
                    md_map (MINOR(bh[i]->b_dev), &bh[i]->b_rdev,
                            &bh[i]->b_rsector, bh[i]->b_size >> 9)) {
                        printk (KERN_ERR
                                "Bad md_map in ll_rw_block\n");
                        goto sorry;
                }
#endif
        }

        // jeśli chcemy zapisać na urządzeniu tylko do odczytu to błąd
        if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
                printk(KERN_NOTICE "Can't write to read-only device %s\n",
                       kdevname(bh[0]->b_dev));
                goto sorry;
        }

        for (i = 0; i < nr; i++) {
                if (bh[i]) {
                        set_bit(BH_Req, &bh[i]->b_state);
                        // obsłuż żšdanie, tzn. utwórz je
                        make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
                }
        }
        return;

      sorry:
// czyszczenie po sobie, niestety nic nie zrobiono
        for (i = 0; i < nr; i++) {
                if (bh[i]) {
                        clear_bit(BH_Dirty, &bh[i]->b_state);
                        clear_bit(BH_Uptodate, &bh[i]->b_state);
                }
        }
        return;
}

void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
{
        int i, j;
        int buffersize;
        int max_req;
        unsigned long rsector;
        kdev_t rdev;
        struct request * req[8];
        unsigned int major = MAJOR(dev);
// semafor blokujący dostępu do żądania innym "chętnym" którzy chcieliby
// dołączyć swoje żądania do bieżącego
        struct semaphore sem = MUTEX_LOCKED;

// kontroa poprawności podanego urządzenia
// czy jest, czy ma procedurę strategii
        if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
                printk(KERN_NOTICE "ll_rw_swap_file: trying to swap to"
                                   " nonexistent block-device\n");
                return;
        }
// wykorzystujemy całą tablicę all_requests dla żądań czytania
// tylko 2/3 dla żądań pisania
        max_req = NR_REQUEST;
        switch (rw) {
                case READ:
                        break;
                case WRITE:
                        max_req = (NR_REQUEST * 2) / 3;
                        if (is_read_only(dev)) {
                                printk(KERN_NOTICE
                                       "Can't swap to read-only device %s\n",
                                        kdevname(dev));
                                return;
                        }
                        break;
                default:
                        panic("ll_rw_swap: bad block dev cmd, must be R/W");
        }
// długość sektora na dysku wymiany
        buffersize = PAGE_SIZE / nb;

        if (major == LOOP_MAJOR)
             max_req >>= 1;
// obsługuj po conajmniej jednym żšdaniu, tzn. najpierw czekamy 
// na wolne miejsce w all_requests (je?li go nie ma)
// potem wywołujemy 'bottom half' dla dysku
// to odczytywanie lub pisanie musi zostać wykonane ze szczególnym priorytetem

        for (j=0, i=0; i> 9);
#ifdef CONFIG_BLK_DEV_MD
                        if (major==MD_MAJOR &&
                            md_map (MINOR(dev), &rdev,
                                    &rsector, buffersize >> 9)) {
                                printk (KERN_ERR
                                        "Bad md_map in ll_rw_swap_file\n");
                                return;
                        }
#endif
                        
                        // jeśli żadne żądanie nie czeka na obsłużenie to czekaj na wolne miejsca
                        // wpp. próbuj utworzyć nowe żądanie, jeśli się nie powiedzie wywołaj 
                        // bottom half dysku (wyskocz z wewnętrznej pętli)
                        if (j == 0) {
                                req[j] = get_request_wait(max_req, rdev);
                        } else {
                                cli();
                                req[j] = get_request(max_req, rdev);
                                sti();
                                if (req[j] == NULL)
                                        break;
                        }
                        // wpisz dane dotyczące żądania
                        req[j]->cmd = rw;
                        req[j]->errors = 0;
                        req[j]->sector = rsector;
                        req[j]->nr_sectors = buffersize >> 9;
                        req[j]->current_nr_sectors = buffersize >> 9;
                        req[j]->buffer = buf;
                        req[j]->sem = &sem;
                        req[j]->bh = NULL;
                        req[j]->next = NULL;
                        // umieść już utworzone żądanie w kolejce żądań
                        add_request(MAJOR(rdev)+blk_dev,req[j]);
                }
                run_task_queue(&tq_disk);
                // pobrano potrzebne dane - opuść semafor
                while (j > 0) {
                        j--;
                        down(&sem);
                }
        }
}

int blk_dev_init(void)
// inicjowanie urządzeń blokowych
{
        struct request * req;
        struct blk_dev_struct *dev;

// ustawienie zmiennych dotyczących procedury strategii, 
// plug (procedury zatrzymującej pobieranie żądań do wykonania)
// unplug, kolejki żądań dla każdego urządzenia.
        for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) {
                dev->request_fn      = NULL;
                dev->current_request = NULL;
                dev->plug.rq_status  = RQ_INACTIVE;
                dev->plug.cmd        = -1;
                dev->plug.next       = NULL;
                dev->plug_tq.routine = &unplug_device;
                dev->plug_tq.data    = dev;
        }

// zainicjuj tablicę żądań
        req = all_requests + NR_REQUEST;
        while (--req >= all_requests) {
                req->rq_status = RQ_INACTIVE;
                req->next = NULL;
        }
        memset(ro_bits,0,sizeof(ro_bits));
// zainicjuj odpowiednie urządzenia blokowe będące w systemie
#ifdef CONFIG_BLK_DEV_RAM
        rd_init();
#endif
#ifdef CONFIG_BLK_DEV_LOOP
        loop_init();
#endif
#ifdef CONFIG_CDI_INIT
        cdi_init();             /* this MUST precede ide_init */
#endif CONFIG_CDI_INIT
#ifdef CONFIG_BLK_DEV_IDE
        ide_init();             /* this MUST precede hd_init */
#endif
#ifdef CONFIG_BLK_DEV_HD
        hd_init();
#endif
#ifdef CONFIG_BLK_DEV_XD
        xd_init();
#endif
#ifdef CONFIG_BLK_DEV_FD
        floppy_init();
#else
        outb_p(0xc, 0x3f2);
#endif
#ifdef CONFIG_CDU31A
        cdu31a_init();
#endif CONFIG_CDU31A
#ifdef CONFIG_MCD
        mcd_init();
#endif CONFIG_MCD
#ifdef CONFIG_MCDX
        mcdx_init();
#endif CONFIG_MCDX
#ifdef CONFIG_SBPCD
        sbpcd_init();
#endif CONFIG_SBPCD
#ifdef CONFIG_AZTCD
        aztcd_init();
#endif CONFIG_AZTCD
#ifdef CONFIG_CDU535
        sony535_init();
#endif CONFIG_CDU535
#ifdef CONFIG_GSCD
        gscd_init();
#endif CONFIG_GSCD
#ifdef CONFIG_CM206
        cm206_init();
#endif
#ifdef CONFIG_OPTCD
        optcd_init();
#endif CONFIG_OPTCD
#ifdef CONFIG_SJCD
        sjcd_init();
#endif CONFIG_SJCD
#ifdef CONFIG_BLK_DEV_MD
        md_init();
#endif CONFIG_BLK_DEV_MD
        return 0;
}