| /* |
| * 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(); |
| } |
| } |