/* * linux/kernel/blk_dev/ll_rw.c * * (C) 1991 Linus Torvalds */ /* * This handles all read/write requests to block devices */ #include <errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <asm/system.h> #include "blk.h" extern long rd_init(long mem_start, int length); /* * The request-struct contains all necessary data * to load a nr of sectors into memory */ struct request request[NR_REQUEST]; /* * used to wait on when there are no free requests */ struct task_struct * wait_for_request = NULL; /* blk_dev_struct is: * do_request-address * next-request */ struct blk_dev_struct blk_dev[NR_BLK_DEV] = { { NULL, NULL }, /* no_dev */ { NULL, NULL }, /* dev mem */ { NULL, NULL }, /* dev fd */ { NULL, NULL }, /* dev hd */ { NULL, NULL }, /* dev ttyx */ { NULL, NULL }, /* dev tty */ { NULL, NULL }, /* dev lp */ { NULL, NULL }, /* dev pipes */ { NULL, NULL }, /* dev sd */ { NULL, NULL } /* dev st */ }; /* * blk_size contains the size of all block-devices: * * blk_size[MAJOR][MINOR] * * if (!blk_size[MAJOR]) then no minor size checking is done. */ int * blk_size[NR_BLK_DEV] = { NULL, NULL, }; static inline void lock_buffer(struct buffer_head * bh) { cli(); while (bh->b_lock) sleep_on(&bh->b_wait); bh->b_lock=1; sti(); } static inline void unlock_buffer(struct buffer_head * bh) { if (!bh->b_lock) printk("ll_rw_block.c: buffer not locked\n\r"); bh->b_lock = 0; wake_up(&bh->b_wait); } /* * add-request adds a request to the linked list. * It disables interrupts so that it can muck with the * request-lists in peace. * * Note that swapping requests always go before other requests, * and are done in the order they appear. */ static void add_request(struct blk_dev_struct * dev, struct request * req) { struct request * tmp; req->next = NULL; cli(); if (req->bh) req->bh->b_dirt = 0; if (!(tmp = dev->current_request)) { dev->current_request = req; (dev->request_fn)(); sti(); return; } for ( ; tmp->next ; tmp = tmp->next) { if (!req->bh) if (tmp->next->bh) break; else continue; if ((IN_ORDER(tmp,req) || !IN_ORDER(tmp,tmp->next)) && IN_ORDER(req,tmp->next)) break; } req->next = tmp->next; tmp->next = req; sti(); } static void make_request(int major,int rw, struct buffer_head * bh) { struct request * req; int rw_ahead; /* WRITEA/READA is special case - it is not really needed, so if the */ /* buffer is locked, we just forget about it, else it's a normal read */ if (rw_ahead = (rw == READA || rw == WRITEA)) { if (bh->b_lock) return; if (rw == READA) rw = READ; else rw = WRITE; } if (rw!=READ && rw!=WRITE) { printk("Bad block dev command, must be R/W/RA/WA\n"); return; } if (blk_size[major]) if (blk_size[major][MINOR(bh->b_dev)] <= bh->b_blocknr) { bh->b_dirt = bh->b_uptodate = 0; return; } lock_buffer(bh); if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { unlock_buffer(bh); return; } repeat: cli(); if (major == 3 && (req = blk_dev[major].current_request)) { while (req = req->next) { if (req->dev == bh->b_dev && !req->waiting && req->cmd == rw && req->sector + req->nr_sectors == bh->b_blocknr << 1 && req->nr_sectors < 254) { req->bhtail->b_reqnext = bh; req->bhtail = bh; req->nr_sectors += 2; bh->b_dirt = 0; sti(); return; } } } /* we don't allow the write-requests to fill up the queue completely: * we want some room for reads: they take precedence. The last third * of the requests are only for reads. */ if (rw == READ) req = request+NR_REQUEST; else req = request+(NR_REQUEST/2); /* find an empty request */ while (--req >= request) if (req->dev < 0) goto found; /* if none found, sleep on new requests: check for rw_ahead */ if (rw_ahead) { sti(); unlock_buffer(bh); return; } sleep_on(&wait_for_request); sti(); goto repeat; found: sti(); /* fill up the request-info, and add it to the queue */ req->dev = bh->b_dev; req->cmd = rw; req->errors=0; req->sector = bh->b_blocknr<<1; req->nr_sectors = 2; req->buffer = bh->b_data; req->waiting = NULL; req->bh = bh; req->bhtail = bh; req->next = NULL; add_request(major+blk_dev,req); } void ll_rw_page(int rw, int dev, int page, char * buffer) { struct request * req; unsigned int major = MAJOR(dev); if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { printk("Trying to read nonexistent block-device\n\r"); return; } if (rw!=READ && rw!=WRITE) panic("Bad block dev command, must be R/W"); cli(); repeat: req = request+NR_REQUEST; while (--req >= request) if (req->dev<0) break; if (req < request) { sleep_on(&wait_for_request); goto repeat; } sti(); /* fill up the request-info, and add it to the queue */ req->dev = dev; req->cmd = rw; req->errors = 0; req->sector = page<<3; req->nr_sectors = 8; req->buffer = buffer; req->waiting = current; req->bh = NULL; req->next = NULL; current->state = TASK_UNINTERRUPTIBLE; add_request(major+blk_dev,req); schedule(); } void ll_rw_block(int rw, struct buffer_head * bh) { unsigned int major; if (!bh) return; if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { printk("ll_rw_block: Trying to read nonexistent block-device\n\r"); return; } make_request(major,rw,bh); } long blk_dev_init(long mem_start, long mem_end) { int i; for (i=0 ; i<NR_REQUEST ; i++) { request[i].dev = -1; request[i].next = NULL; } #ifdef RAMDISK mem_start += rd_init(mem_start, RAMDISK*1024); #endif return mem_start; } void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf) { int i; struct request * req; unsigned int major = MAJOR(dev); if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { printk("ll_rw_swap_file: trying to swap nonexistent block-device\n\r"); return; } if (rw!=READ && rw!=WRITE) { printk("ll_rw_swap: bad block dev command, must be R/W"); return; } for (i=0; i<nb; i++, buf += BLOCK_SIZE) { repeat: req = request+NR_REQUEST; while (--req >= request) if (req->dev<0) break; if (req < request) { sleep_on(&wait_for_request); goto repeat; } req->dev = dev; req->cmd = rw; req->errors = 0; req->sector = b[i] << 1; req->nr_sectors = 2; req->buffer = buf; req->waiting = current; req->bh = NULL; req->next = NULL; current->state = TASK_UNINTERRUPTIBLE; add_request(major+blk_dev,req); schedule(); } }