patch-1.3.69 linux/mm/filemap.c
Next file: linux/mm/kmalloc.c
Previous file: linux/kernel/sysctl.c
Back to the patch index
Back to the overall index
- Lines: 155
- Date:
Tue Feb 27 11:06:13 1996
- Orig file:
v1.3.68/linux/mm/filemap.c
- Orig date:
Fri Feb 23 13:54:41 1996
diff -u --recursive --new-file v1.3.68/linux/mm/filemap.c linux/mm/filemap.c
@@ -293,19 +293,24 @@
* This is a generic file read routine, and uses the
* inode->i_op->readpage() function for the actual low-level
* stuff.
+ *
+ * This is really ugly. But the goto's actually try to clarify some
+ * of the logic when it comes to error handling etc.
*/
#define MAX_READAHEAD (PAGE_SIZE*4)
int generic_file_read(struct inode * inode, struct file * filp, char * buf, int count)
{
- int read = 0;
- unsigned long pos;
- unsigned long page_cache = 0;
+ int error, read;
+ unsigned long pos, page_cache;
if (count <= 0)
return 0;
+ error = 0;
+ read = 0;
+ page_cache = 0;
pos = filp->f_pos;
- do {
+ for (;;) {
struct page *page;
unsigned long offset, addr, nr;
@@ -324,14 +329,14 @@
* Ok, it wasn't cached, so we need to create a new
* page..
*/
- if (!page_cache) {
- page_cache = __get_free_page(GFP_KERNEL);
- if (!page_cache) {
- if (!read)
- read = -ENOMEM;
- break;
- }
- }
+ if (page_cache)
+ goto new_page;
+
+ error = -ENOMEM;
+ page_cache = __get_free_page(GFP_KERNEL);
+ if (!page_cache)
+ break;
+ error = 0;
/*
* That could have slept, so we need to check again..
@@ -339,22 +344,8 @@
if (pos >= inode->i_size)
break;
page = find_page(inode, pos & PAGE_MASK);
- if (page)
- goto found_page;
-
- /*
- * Ok, add the new page to the hash-queues...
- */
- page = mem_map + MAP_NR(page_cache);
- page_cache = 0;
- page->count++;
- page->uptodate = 0;
- page->error = 0;
- page->offset = pos & PAGE_MASK;
- add_page_to_inode_queue(inode, page);
- add_page_to_hash_queue(inode, page);
-
- inode->i_op->readpage(inode, page);
+ if (!page)
+ goto new_page;
found_page:
addr = page_address(page);
@@ -369,15 +360,20 @@
* - if "f_reada" is set
*/
if (page->locked) {
- if (nr < count || filp->f_reada) {
- unsigned long ahead = 0;
- do {
- ahead += PAGE_SIZE;
- page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
- } while (ahead < MAX_READAHEAD);
+ unsigned long max_ahead, ahead;
+
+ max_ahead = count - nr;
+ if (filp->f_reada || max_ahead > MAX_READAHEAD)
+ max_ahead = MAX_READAHEAD;
+ ahead = 0;
+ while (ahead < max_ahead) {
+ ahead += PAGE_SIZE;
+ page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
}
__wait_on_page(page);
}
+ if (!page->uptodate)
+ goto read_page;
if (nr > inode->i_size - pos)
nr = inode->i_size - pos;
memcpy_tofs(buf, (void *) (addr + offset), nr);
@@ -386,7 +382,40 @@
pos += nr;
read += nr;
count -= nr;
- } while (count);
+ if (count)
+ continue;
+ break;
+
+
+new_page:
+ /*
+ * Ok, add the new page to the hash-queues...
+ */
+ addr = page_cache;
+ page = mem_map + MAP_NR(page_cache);
+ page_cache = 0;
+ page->count++;
+ page->uptodate = 0;
+ page->error = 0;
+ page->offset = pos & PAGE_MASK;
+ add_page_to_inode_queue(inode, page);
+ add_page_to_hash_queue(inode, page);
+
+ /*
+ * Error handling is tricky. If we get a read error,
+ * the cached page stays in the cache (but uptodate=0),
+ * and the next process that accesses it will try to
+ * re-read it. This is needed for NFS etc, where the
+ * identity of the reader can decide if we can read the
+ * page or not..
+ */
+read_page:
+ error = inode->i_op->readpage(inode, page);
+ if (!error)
+ goto found_page;
+ free_page(addr);
+ break;
+ }
filp->f_pos = pos;
filp->f_reada = 1;
@@ -396,6 +425,8 @@
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
+ if (!read)
+ read = error;
return read;
}
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