patch-1.3.53 linux/fs/ext2/file.c
Next file: linux/fs/filesystems.c
Previous file: linux/fs/Makefile
Back to the patch index
Back to the overall index
- Lines: 293
- Date:
Tue Jan 2 08:14:43 1996
- Orig file:
v1.3.52/linux/fs/ext2/file.c
- Orig date:
Tue Dec 26 04:45:37 1995
diff -u --recursive --new-file v1.3.52/linux/fs/ext2/file.c linux/fs/ext2/file.c
@@ -25,6 +25,8 @@
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
#define NBUF 32
@@ -34,6 +36,7 @@
#include <linux/fs.h>
#include <linux/ext2_fs.h>
+static int ext2_readpage(struct inode *, unsigned long, char *);
static int ext2_file_read (struct inode *, struct file *, char *, int);
static int ext2_file_write (struct inode *, struct file *, const char *, int);
static void ext2_release_file (struct inode *, struct file *);
@@ -71,7 +74,7 @@
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
- NULL, /* readpage */
+ ext2_readpage, /* readpage */
NULL, /* writepage */
ext2_bmap, /* bmap */
ext2_truncate, /* truncate */
@@ -79,150 +82,101 @@
NULL /* smap */
};
-static int ext2_file_read (struct inode * inode, struct file * filp,
- char * buf, int count)
+static int ext2_readpage(struct inode * inode, unsigned long offset, char * page)
{
- int read, left, chars;
- int block, blocks, offset;
- int bhrequest, uptodate;
- int clusterblocks;
- struct buffer_head ** bhb, ** bhe;
- struct buffer_head * bhreq[NBUF];
- struct buffer_head * buflist[NBUF];
- struct super_block * sb;
- unsigned int size;
- int err;
-
- if (!inode) {
- printk ("ext2_file_read: inode = NULL\n");
- return -EINVAL;
- }
- sb = inode->i_sb;
- if (!S_ISREG(inode->i_mode)) {
- ext2_warning (sb, "ext2_file_read", "mode = %07o",
- inode->i_mode);
- return -EINVAL;
- }
- offset = filp->f_pos;
- size = inode->i_size;
- if (offset > size)
- left = 0;
- else
- left = size - offset;
- if (left > count)
- left = count;
- if (left <= 0)
- return 0;
- read = 0;
- block = offset >> EXT2_BLOCK_SIZE_BITS(sb);
- offset &= (sb->s_blocksize - 1);
- chars = sb->s_blocksize - offset;
- size = (size + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
- blocks = (left + offset + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
- bhb = bhe = buflist;
- if (filp->f_reada) {
- if (blocks < read_ahead[MAJOR(inode->i_dev)] >> (EXT2_BLOCK_SIZE_BITS(sb) - 9))
- blocks = read_ahead[MAJOR(inode->i_dev)] >> (EXT2_BLOCK_SIZE_BITS(sb) - 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.
- */
-
- clusterblocks = 0;
+ int *p, nr[PAGE_SIZE/512];
+ int i;
+ i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+ offset >>= inode->i_sb->s_blocksize_bits;
+ p = nr;
do {
- bhrequest = 0;
- uptodate = 1;
- while (blocks) {
- --blocks;
-#if 1
- if(!clusterblocks) clusterblocks = ext2_getcluster(inode, block);
- if(clusterblocks) clusterblocks--;
-#endif
-
- *bhb = ext2_getblk (inode, block++, 0, &err);
- if (*bhb && !buffer_uptodate(*bhb)) {
- uptodate = 0;
- bhreq[bhrequest++] = *bhb;
- }
+ *p = ext2_bmap(inode, offset);
+ i--;
+ offset++;
+ p++;
+ } while (i > 0);
+ return bread_page((unsigned long) page, inode->i_dev, nr, inode->i_sb->s_blocksize);
+}
- if (++bhb == &buflist[NBUF])
- bhb = buflist;
+/*
+ * This is a generic file read routine, and uses the
+ * inode->i_op->readpage() function for the actual low-level
+ * stuff. We can put this into the other filesystems too
+ * once we've debugged it a bit more.
+ */
+static int ext2_file_read (struct inode * inode, struct file * filp,
+ char * buf, int count)
+{
+ int read = 0;
+ unsigned long pos;
+ unsigned long addr;
+ unsigned long cached_page = 0;
+ struct page *page;
- /*
- * If the block we have on hand is uptodate, go ahead
- * and complete processing
- */
- if (uptodate)
- break;
+ if (count <= 0)
+ return 0;
- if (bhb == bhe)
- break;
- }
+ pos = filp->f_pos;
+
+ for (;;) {
+ unsigned long offset, nr;
- /*
- * Now request them all
- */
- if (bhrequest)
- ll_rw_block (READ, bhrequest, bhreq);
+ if (pos >= inode->i_size)
+ break;
- do {
- /*
- * Finish off all I/O that has actually completed
- */
- if (*bhe) {
- wait_on_buffer (*bhe);
- if (!buffer_uptodate(*bhe)) { /* read error? */
- brelse(*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- left = 0;
- break;
- }
- }
- left -= chars;
- if (left < 0)
- chars += left;
- filp->f_pos += chars;
- read += chars;
- if (*bhe) {
- memcpy_tofs (buf, offset + (*bhe)->b_data,
- chars);
- brelse (*bhe);
- buf += chars;
- } else {
- while (chars-- > 0)
- put_user (0, buf++);
+ offset = pos & ~PAGE_MASK;
+ nr = PAGE_SIZE - offset;
+ if (nr > count)
+ nr = count;
+
+ /* is it already cached? */
+ page = find_page(inode, pos & PAGE_MASK);
+ if (page)
+ goto found_page;
+
+ /* not cached, have to read it in.. */
+ if (!(addr = cached_page)) {
+ addr = cached_page = __get_free_page(GFP_KERNEL);
+ if (!addr) {
+ if (!read)
+ read = -ENOMEM;
+ break;
}
- offset = 0;
- chars = sb->s_blocksize;
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
- } while (left > 0);
+ }
+ inode->i_op->readpage(inode, pos & PAGE_MASK, (char *) addr);
- /*
- * Release the read-ahead blocks
- */
- while (bhe != bhb) {
- brelse (*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
+ /* while we did that, things may have changed.. */
+ if (pos >= inode->i_size)
+ break;
+ page = find_page(inode, pos & PAGE_MASK);
+ if (page)
+ goto found_page;
+
+ /* nope, this is the only copy.. */
+ cached_page = 0;
+ page = mem_map + MAP_NR(addr);
+ page->offset = pos & PAGE_MASK;
+ add_page_to_inode_queue(inode, page);
+ add_page_to_hash_queue(inode, page);
+
+found_page:
+ if (nr > inode->i_size - pos)
+ nr = inode->i_size - pos;
+ page->count++;
+ addr = page_address(page);
+ memcpy_tofs(buf, (void *) (addr + offset), nr);
+ free_page(addr);
+ buf += nr;
+ pos += nr;
+ read += nr;
+ count -= nr;
+ if (!count)
+ break;
}
- if (!read)
- return -EIO;
- filp->f_reada = 1;
+ filp->f_pos = pos;
+ if (cached_page)
+ free_page(cached_page);
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
@@ -230,6 +184,18 @@
return read;
}
+static inline void update_vm_cache(struct inode * inode, unsigned long pos,
+ char * buf, int count)
+{
+ struct page * page;
+
+ page = find_page(inode, pos & PAGE_MASK);
+ if (page) {
+ pos = (pos & ~PAGE_MASK) + page_address(page);
+ memcpy((void *) pos, buf, count);
+ }
+}
+
static int ext2_file_write (struct inode * inode, struct file * filp,
const char * buf, int count)
{
@@ -303,10 +269,11 @@
break;
}
}
+ memcpy_fromfs (bh->b_data + offset, buf, c);
+ update_vm_cache(inode, pos, bh->b_data + offset, c);
pos2 += c;
pos += c;
written += c;
- memcpy_fromfs (bh->b_data + offset, buf, c);
buf += c;
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this