You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
3.5 KiB
166 lines
3.5 KiB
3 years ago
|
/*
|
||
|
* 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"
|
||
|
|
||
|
/*
|
||
|
* 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 */
|
||
|
};
|
||
|
|
||
|
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.
|
||
|
*/
|
||
|
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;
|
||
|
sti();
|
||
|
(dev->request_fn)();
|
||
|
return;
|
||
|
}
|
||
|
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;
|
||
|
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)
|
||
|
panic("Bad block dev command, must be R/W/RA/WA");
|
||
|
lock_buffer(bh);
|
||
|
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
|
||
|
unlock_buffer(bh);
|
||
|
return;
|
||
|
}
|
||
|
repeat:
|
||
|
/* 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)/3);
|
||
|
/* find an empty request */
|
||
|
while (--req >= request)
|
||
|
if (req->dev<0)
|
||
|
break;
|
||
|
/* if none found, sleep on new requests: check for rw_ahead */
|
||
|
if (req < request) {
|
||
|
if (rw_ahead) {
|
||
|
unlock_buffer(bh);
|
||
|
return;
|
||
|
}
|
||
|
sleep_on(&wait_for_request);
|
||
|
goto repeat;
|
||
|
}
|
||
|
/* 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->next = NULL;
|
||
|
add_request(major+blk_dev,req);
|
||
|
}
|
||
|
|
||
|
void ll_rw_block(int rw, struct buffer_head * bh)
|
||
|
{
|
||
|
unsigned int major;
|
||
|
|
||
|
if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
|
||
|
!(blk_dev[major].request_fn)) {
|
||
|
printk("Trying to read nonexistent block-device\n\r");
|
||
|
return;
|
||
|
}
|
||
|
make_request(major,rw,bh);
|
||
|
}
|
||
|
|
||
|
void blk_dev_init(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i=0 ; i<NR_REQUEST ; i++) {
|
||
|
request[i].dev = -1;
|
||
|
request[i].next = NULL;
|
||
|
}
|
||
|
}
|