block_dev.c 7.4 KB
Newer Older
1 2 3
/*
 *  linux/fs/block_dev.c
 *
4
 *  Copyright (C) 1991, 1992  Linus Torvalds
5 6
 */

7
#include <linux/errno.h>
8
#include <linux/sched.h>
9
#include <linux/kernel.h>
10
#include <linux/locks.h>
Linus Torvalds's avatar
Linus Torvalds committed
11
#include <linux/fcntl.h>
12
#include <asm/segment.h>
13
#include <asm/system.h>
14

Linus Torvalds's avatar
Linus Torvalds committed
15
extern int *blk_size[];
16
extern int *blksize_size[];
Linus Torvalds's avatar
Linus Torvalds committed
17

Linus Torvalds's avatar
Linus Torvalds committed
18 19
#define NBUF 64

20
int block_write(struct inode * inode, struct file * filp, char * buf, int count)
21
{
Linus Torvalds's avatar
Linus Torvalds committed
22
	int blocksize, blocksize_bits, i, j, buffercount,write_error;
Linus Torvalds's avatar
Linus Torvalds committed
23
	int block, blocks;
Linus Torvalds's avatar
Linus Torvalds committed
24
	loff_t offset;
25 26
	int chars;
	int written = 0;
Linus Torvalds's avatar
Linus Torvalds committed
27
	int cluster_list[8];
Linus Torvalds's avatar
Linus Torvalds committed
28 29
	struct buffer_head * bhlist[NBUF];
	int blocks_per_cluster;
30
	unsigned int size;
31
	unsigned int dev;
Linus Torvalds's avatar
Linus Torvalds committed
32
	struct buffer_head * bh, *bufferlist[NBUF];
33
	register char * p;
Linus Torvalds's avatar
Linus Torvalds committed
34
	int excess;
35

Linus Torvalds's avatar
Linus Torvalds committed
36
	write_error = buffercount = 0;
37
	dev = inode->i_rdev;
Linus Torvalds's avatar
Linus Torvalds committed
38 39
	if ( is_read_only( inode->i_rdev ))
		return -EPERM;
40 41 42 43 44 45 46 47 48 49 50
	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;
	}

Linus Torvalds's avatar
Linus Torvalds committed
51 52
	blocks_per_cluster = PAGE_SIZE / blocksize;

53 54 55
	block = filp->f_pos >> blocksize_bits;
	offset = filp->f_pos & (blocksize-1);

Linus Torvalds's avatar
Linus Torvalds committed
56
	if (blk_size[MAJOR(dev)])
Linus Torvalds's avatar
Linus Torvalds committed
57
		size = ((loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
Linus Torvalds's avatar
Linus Torvalds committed
58
	else
59
		size = INT_MAX;
60
	while (count>0) {
Linus Torvalds's avatar
Linus Torvalds committed
61
		if (block >= size)
62
			return written;
63
		chars = blocksize - offset;
64 65
		if (chars > count)
			chars=count;
Linus Torvalds's avatar
Linus Torvalds committed
66 67

#if 0
68 69
		if (chars == blocksize)
			bh = getblk(dev, block, blocksize);
70
		else
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
			bh = breada(dev,block,block+1,block+2,-1);

#else
		for(i=0; i<blocks_per_cluster; i++) cluster_list[i] = block+i;
		if((block % blocks_per_cluster) == 0)
		  generate_cluster(dev, cluster_list, blocksize);
		bh = getblk(dev, block, blocksize);

		if (chars != blocksize && !bh->b_uptodate) {
		  if(!filp->f_reada ||
		     !read_ahead[MAJOR(dev)]) {
		    /* We do this to force the read of a single buffer */
		    brelse(bh);
		    bh = bread(dev,block,blocksize);
		  } else {
		    /* Read-ahead before write */
		    blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2;
		    if (block + blocks > size) blocks = size - block;
		    if (blocks > NBUF) blocks=NBUF;
Linus Torvalds's avatar
Linus Torvalds committed
90 91 92
		    excess = (block + blocks) % blocks_per_cluster;
		    if ( blocks > excess )
			blocks -= excess;
Linus Torvalds's avatar
Linus Torvalds committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106
		    bhlist[0] = bh;
		    for(i=1; i<blocks; i++){
		      if(((i+block) % blocks_per_cluster) == 0) {
			for(j=0; j<blocks_per_cluster; j++) cluster_list[j] = block+i+j;
			generate_cluster(dev, cluster_list, blocksize);
		      };
		      bhlist[i] = getblk (dev, block+i, blocksize);
		      if(!bhlist[i]){
			while(i >= 0) brelse(bhlist[i--]);
			return written? written: -EIO;
		      };
		    };
		    ll_rw_block(READ, blocks, bhlist);
		    for(i=1; i<blocks; i++) brelse(bhlist[i]);
Linus Torvalds's avatar
Linus Torvalds committed
107
		    wait_on_buffer(bh);
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110 111
		      
		  };
		};
#endif
112
		block++;
113 114 115 116
		if (!bh)
			return written?written:-EIO;
		p = offset + bh->b_data;
		offset = 0;
117
		filp->f_pos += chars;
118 119
		written += chars;
		count -= chars;
120 121 122
		memcpy_fromfs(p,buf,chars);
		p += chars;
		buf += chars;
123
		bh->b_uptodate = 1;
Linus Torvalds's avatar
Linus Torvalds committed
124
		mark_buffer_dirty(bh, 0);
Linus Torvalds's avatar
Linus Torvalds committed
125 126 127 128 129 130 131 132 133 134 135 136 137
		if (filp->f_flags & O_SYNC)
			bufferlist[buffercount++] = bh;
		else
			brelse(bh);
		if (buffercount == NBUF){
			ll_rw_block(WRITE, buffercount, bufferlist);
			for(i=0; i<buffercount; i++){
				wait_on_buffer(bufferlist[i]);
				if (!bufferlist[i]->b_uptodate)
					write_error=1;
				brelse(bufferlist[i]);
			}
			buffercount=0;
Linus Torvalds's avatar
Linus Torvalds committed
138
		}
Linus Torvalds's avatar
Linus Torvalds committed
139 140
		if(write_error)
			break;
141
	}
Linus Torvalds's avatar
Linus Torvalds committed
142 143 144 145 146 147 148 149 150
	if ( buffercount ){
		ll_rw_block(WRITE, buffercount, bufferlist);
		for(i=0; i<buffercount; i++){
			wait_on_buffer(bufferlist[i]);
			if (!bufferlist[i]->b_uptodate)
				write_error=1;
			brelse(bufferlist[i]);
		}
	}		
Linus Torvalds's avatar
Linus Torvalds committed
151
	filp->f_reada = 1;
Linus Torvalds's avatar
Linus Torvalds committed
152 153
	if(write_error)
		return -EIO;
154 155 156
	return written;
}

157
int block_read(struct inode * inode, struct file * filp, char * buf, int count)
158
{
159
	unsigned int block;
Linus Torvalds's avatar
Linus Torvalds committed
160
	loff_t offset;
161 162
	int blocksize;
	int blocksize_bits, i;
Linus Torvalds's avatar
Linus Torvalds committed
163
	unsigned int blocks, rblocks, left;
164
	int bhrequest, uptodate;
Linus Torvalds's avatar
Linus Torvalds committed
165
	int cluster_list[8];
Linus Torvalds's avatar
Linus Torvalds committed
166
	int blocks_per_cluster;
167 168 169
	struct buffer_head ** bhb, ** bhe;
	struct buffer_head * buflist[NBUF];
	struct buffer_head * bhreq[NBUF];
170
	unsigned int chars;
Linus Torvalds's avatar
Linus Torvalds committed
171
	loff_t size;
172
	unsigned int dev;
173
	int read;
Linus Torvalds's avatar
Linus Torvalds committed
174
	int excess;
175

176
	dev = inode->i_rdev;
177 178 179 180 181 182 183 184 185 186
	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;
	}

187
	offset = filp->f_pos;
Linus Torvalds's avatar
Linus Torvalds committed
188
	if (blk_size[MAJOR(dev)])
Linus Torvalds's avatar
Linus Torvalds committed
189
		size = (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS;
Linus Torvalds's avatar
Linus Torvalds committed
190
	else
191
		size = INT_MAX;
192

Linus Torvalds's avatar
Linus Torvalds committed
193 194
	blocks_per_cluster = PAGE_SIZE / blocksize;

195
	if (offset > size)
196 197
		left = 0;
	else
198
		left = size - offset;
199 200 201 202
	if (left > count)
		left = count;
	if (left <= 0)
		return 0;
203
	read = 0;
204 205 206
	block = offset >> blocksize_bits;
	offset &= blocksize-1;
	size >>= blocksize_bits;
Linus Torvalds's avatar
Linus Torvalds committed
207
	rblocks = blocks = (left + offset + blocksize - 1) >> blocksize_bits;
208
	bhb = bhe = buflist;
209
	if (filp->f_reada) {
Linus Torvalds's avatar
Linus Torvalds committed
210 211
	        if (blocks < read_ahead[MAJOR(dev)] / (blocksize >> 9))
			blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9);
Linus Torvalds's avatar
Linus Torvalds committed
212 213 214
		excess = (block + blocks) % blocks_per_cluster;
		if ( blocks > excess )
			blocks -= excess;		
Linus Torvalds's avatar
Linus Torvalds committed
215 216
		if (rblocks > blocks)
			blocks = rblocks;
Linus Torvalds's avatar
Linus Torvalds committed
217
		
218
	}
Linus Torvalds's avatar
Linus Torvalds committed
219 220
	if (block + blocks > size)
		blocks = size - block;
221 222 223 224 225 226 227 228 229 230 231 232

	/* 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 {
233 234
		bhrequest = 0;
		uptodate = 1;
235 236
		while (blocks) {
			--blocks;
Linus Torvalds's avatar
Linus Torvalds committed
237 238 239 240 241 242
#if 1
			if((block % blocks_per_cluster) == 0) {
			  for(i=0; i<blocks_per_cluster; i++) cluster_list[i] = block+i;
			  generate_cluster(dev, cluster_list, blocksize);
			}
#endif
243
			*bhb = getblk(dev, block++, blocksize);
244
			if (*bhb && !(*bhb)->b_uptodate) {
245 246
				uptodate = 0;
				bhreq[bhrequest++] = *bhb;
247
			}
248 249 250 251 252 253

			if (++bhb == &buflist[NBUF])
				bhb = buflist;

			/* If the block we have on hand is uptodate, go ahead
			   and complete processing. */
254 255
			if (uptodate)
				break;
256 257
			if (bhb == bhe)
				break;
258 259
		}

260
		/* Now request them all */
Linus Torvalds's avatar
Linus Torvalds committed
261
		if (bhrequest) {
262
			ll_rw_block(READ, bhrequest, bhreq);
Linus Torvalds's avatar
Linus Torvalds committed
263 264
			refill_freelist(blocksize);
		}
265 266 267 268 269

		do { /* Finish off all I/O that has actually completed */
			if (*bhe) {
				wait_on_buffer(*bhe);
				if (!(*bhe)->b_uptodate) {	/* read error? */
270 271 272
				        brelse(*bhe);
					if (++bhe == &buflist[NBUF])
					  bhe = buflist;
273 274 275 276
					left = 0;
					break;
				}
			}			
277
			if (left < blocksize - offset)
278 279
				chars = left;
			else
280
				chars = blocksize - offset;
281 282 283 284 285 286 287 288 289 290 291 292
			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;
293
			if (++bhe == &buflist[NBUF])
294 295
				bhe = buflist;
		} while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
296 297 298 299
	} while (left > 0);

/* Release the read-ahead blocks */
	while (bhe != bhb) {
300 301 302
		brelse(*bhe);
		if (++bhe == &buflist[NBUF])
			bhe = buflist;
303 304 305
	};
	if (!read)
		return -EIO;
306
	filp->f_reada = 1;
307 308
	return read;
}
309 310 311 312 313

int block_fsync(struct inode *inode, struct file *filp)
{
	return fsync_dev (inode->i_rdev);
}