215 lines
4.7 KiB
C
215 lines
4.7 KiB
C
/*
|
|
* linux/fs/block_dev.c
|
|
*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
*/
|
|
|
|
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/locks.h>
|
|
#include <asm/segment.h>
|
|
#include <asm/system.h>
|
|
|
|
extern int *blk_size[];
|
|
extern int *blksize_size[];
|
|
|
|
int block_write(struct inode * inode, struct file * filp, char * buf, int count)
|
|
{
|
|
int blocksize, blocksize_bits, i;
|
|
int block;
|
|
int offset;
|
|
int chars;
|
|
int written = 0;
|
|
unsigned int size;
|
|
unsigned int dev;
|
|
struct buffer_head * bh;
|
|
register char * p;
|
|
|
|
dev = inode->i_rdev;
|
|
blocksize = BLOCK_SIZE;
|
|
if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
|
|
blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
|
|
|
|
i = blocksize;
|
|
blocksize_bits = 0;
|
|
while(i != 1) {
|
|
blocksize_bits++;
|
|
i >>= 1;
|
|
}
|
|
|
|
block = filp->f_pos >> blocksize_bits;
|
|
offset = filp->f_pos & (blocksize-1);
|
|
|
|
if (blk_size[MAJOR(dev)])
|
|
size = (blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
|
|
else
|
|
size = INT_MAX;
|
|
while (count>0) {
|
|
if (block >= size)
|
|
return written;
|
|
chars = blocksize - offset;
|
|
if (chars > count)
|
|
chars=count;
|
|
if (chars == blocksize)
|
|
bh = getblk(dev, block, blocksize);
|
|
else
|
|
bh = breada(dev,block,block+1,block+2,-1);
|
|
block++;
|
|
if (!bh)
|
|
return written?written:-EIO;
|
|
p = offset + bh->b_data;
|
|
offset = 0;
|
|
filp->f_pos += chars;
|
|
written += chars;
|
|
count -= chars;
|
|
memcpy_fromfs(p,buf,chars);
|
|
p += chars;
|
|
buf += chars;
|
|
bh->b_uptodate = 1;
|
|
bh->b_dirt = 1;
|
|
brelse(bh);
|
|
}
|
|
return written;
|
|
}
|
|
|
|
#define NBUF 32
|
|
|
|
int block_read(struct inode * inode, struct file * filp, char * buf, int count)
|
|
{
|
|
unsigned int block;
|
|
unsigned int offset;
|
|
int blocksize;
|
|
int blocksize_bits, i;
|
|
unsigned int left;
|
|
unsigned int blocks;
|
|
int bhrequest, uptodate;
|
|
struct buffer_head ** bhb, ** bhe;
|
|
struct buffer_head * buflist[NBUF];
|
|
struct buffer_head * bhreq[NBUF];
|
|
unsigned int chars;
|
|
unsigned int size;
|
|
unsigned int dev;
|
|
int read;
|
|
|
|
dev = inode->i_rdev;
|
|
blocksize = BLOCK_SIZE;
|
|
if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
|
|
blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
|
|
i = blocksize;
|
|
blocksize_bits = 0;
|
|
while (i != 1) {
|
|
blocksize_bits++;
|
|
i >>= 1;
|
|
}
|
|
|
|
offset = filp->f_pos;
|
|
if (blk_size[MAJOR(dev)])
|
|
size = blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS;
|
|
else
|
|
size = INT_MAX;
|
|
|
|
if (offset > size)
|
|
left = 0;
|
|
else
|
|
left = size - offset;
|
|
if (left > count)
|
|
left = count;
|
|
if (left <= 0)
|
|
return 0;
|
|
read = 0;
|
|
block = offset >> blocksize_bits;
|
|
offset &= blocksize-1;
|
|
size >>= blocksize_bits;
|
|
blocks = (left + offset + blocksize - 1) >> blocksize_bits;
|
|
bhb = bhe = buflist;
|
|
if (filp->f_reada) {
|
|
blocks += read_ahead[MAJOR(dev)] / (blocksize >> 9);
|
|
if (block + blocks > size)
|
|
blocks = size - block;
|
|
}
|
|
|
|
/* We do this in a two stage process. We first try and request
|
|
as many blocks as we can, then we wait for the first one to
|
|
complete, and then we try and 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. */
|
|
|
|
do {
|
|
bhrequest = 0;
|
|
uptodate = 1;
|
|
while (blocks) {
|
|
--blocks;
|
|
*bhb = getblk(dev, block++, blocksize);
|
|
if (*bhb && !(*bhb)->b_uptodate) {
|
|
uptodate = 0;
|
|
bhreq[bhrequest++] = *bhb;
|
|
}
|
|
|
|
if (++bhb == &buflist[NBUF])
|
|
bhb = buflist;
|
|
|
|
/* If the block we have on hand is uptodate, go ahead
|
|
and complete processing. */
|
|
if (uptodate)
|
|
break;
|
|
if (bhb == bhe)
|
|
break;
|
|
}
|
|
|
|
/* Now request them all */
|
|
if (bhrequest)
|
|
ll_rw_block(READ, bhrequest, bhreq);
|
|
|
|
do { /* Finish off all I/O that has actually completed */
|
|
if (*bhe) {
|
|
wait_on_buffer(*bhe);
|
|
if (!(*bhe)->b_uptodate) { /* read error? */
|
|
brelse(*bhe);
|
|
if (++bhe == &buflist[NBUF])
|
|
bhe = buflist;
|
|
left = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (left < blocksize - offset)
|
|
chars = left;
|
|
else
|
|
chars = blocksize - offset;
|
|
filp->f_pos += chars;
|
|
left -= chars;
|
|
read += chars;
|
|
if (*bhe) {
|
|
memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
|
|
brelse(*bhe);
|
|
buf += chars;
|
|
} else {
|
|
while (chars-->0)
|
|
put_fs_byte(0,buf++);
|
|
}
|
|
offset = 0;
|
|
if (++bhe == &buflist[NBUF])
|
|
bhe = buflist;
|
|
} while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
|
|
} while (left > 0);
|
|
|
|
/* Release the read-ahead blocks */
|
|
while (bhe != bhb) {
|
|
brelse(*bhe);
|
|
if (++bhe == &buflist[NBUF])
|
|
bhe = buflist;
|
|
};
|
|
if (!read)
|
|
return -EIO;
|
|
filp->f_reada = 1;
|
|
return read;
|
|
}
|
|
|
|
int block_fsync(struct inode *inode, struct file *filp)
|
|
{
|
|
return fsync_dev (inode->i_rdev);
|
|
}
|