patch-2.1.47 linux/mm/filemap.c
Next file: linux/mm/vmscan.c
Previous file: linux/kernel/ksyms.c
Back to the patch index
Back to the overall index
- Lines: 157
- Date:
Wed Jul 23 12:53:04 1997
- Orig file:
v2.1.46/linux/mm/filemap.c
- Orig date:
Thu Jul 17 10:06:09 1997
diff -u --recursive --new-file v2.1.46/linux/mm/filemap.c linux/mm/filemap.c
@@ -43,18 +43,7 @@
* Simple routines for both non-shared and shared mappings.
*/
-/*
- * This is a special fast page-free routine that _only_ works
- * on page-cache pages that we are currently using. We can
- * just decrement the page count, because we know that the page
- * has a count > 1 (the page cache itself counts as one, and
- * we're currently using it counts as one). So we don't need
- * the full free_page() stuff..
- */
-static inline void release_page(struct page * page)
-{
- atomic_dec(&page->count);
-}
+#define release_page(page) __free_page((page))
/*
* Invalidate the pages of an inode, removing all pages that aren't
@@ -767,6 +756,9 @@
* The goto's are kind of ugly, but this streamlines the normal case of having
* it in the page cache, and handles the special cases reasonably without
* having a lot of duplicated code.
+ *
+ * WSH 06/04/97: fixed a memory leak and moved the allocation of new_page
+ * ahead of the wait if we're sure to need it.
*/
static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share)
{
@@ -791,8 +783,15 @@
found_page:
/*
* Ok, found a page in the page cache, now we need to check
- * that it's up-to-date
+ * that it's up-to-date. First check whether we'll need an
+ * extra page -- better to overlap the allocation with the I/O.
*/
+ if (no_share && !new_page) {
+ new_page = __get_free_page(GFP_KERNEL);
+ if (!new_page)
+ goto failure;
+ }
+
if (PageLocked(page))
goto page_locked_wait;
if (!PageUptodate(page))
@@ -817,13 +816,8 @@
}
/*
- * Check that we have another page to copy it over to..
+ * No sharing ... copy to the new page.
*/
- if (!new_page) {
- new_page = __get_free_page(GFP_KERNEL);
- if (!new_page)
- goto failure;
- }
copy_page(new_page, old_page);
flush_page_to_ram(new_page);
release_page(page);
@@ -887,6 +881,8 @@
*/
failure:
release_page(page);
+ if (new_page)
+ free_page(new_page);
no_page:
return 0;
}
@@ -935,6 +931,10 @@
/* whee.. just mark the buffer heads dirty */
struct buffer_head * tmp = bh;
do {
+ /*
+ * WSH: There's a race here: mark_buffer_dirty()
+ * could block, and the buffers aren't pinned down.
+ */
mark_buffer_dirty(tmp, 0);
tmp = tmp->b_this_page;
} while (tmp != bh);
@@ -953,6 +953,9 @@
file.f_pos = offset;
file.f_reada = 0;
+ /*
+ * WSH: could vm_area struct (and inode) be released while writing?
+ */
down(&inode->i_sem);
result = do_write_page(inode, &file, (const char *) page, offset);
up(&inode->i_sem);
@@ -1295,7 +1298,7 @@
unsigned long ppos, offset;
unsigned int bytes, written;
unsigned long pos;
- int status, sync, didread = 0;
+ int status, sync, didread;
if (!inode->i_op || !inode->i_op->updatepage)
return -EIO;
@@ -1334,9 +1337,14 @@
page_cache = 0;
}
-lockit:
- while (test_and_set_bit(PG_locked, &page->flags))
- wait_on_page(page);
+ /*
+ * WSH 06/05/97: restructured slightly to make sure we release
+ * the page on an error exit. Removed explicit setting of
+ * PG_locked, as that's handled below the i_op->xxx interface.
+ */
+ didread = 0;
+page_wait:
+ wait_on_page(page);
/*
* If the page is not uptodate, and we're writing less
@@ -1345,28 +1353,24 @@
* after the current end of file.
*/
if (!PageUptodate(page)) {
- /* Already tried to read it twice... too bad */
- if (didread > 1) {
- status = -EIO;
- break;
- }
if (bytes < PAGE_SIZE && ppos < inode->i_size) {
- /* readpage implicitly unlocks the page */
- status = inode->i_op->readpage(inode, page);
+ if (didread < 2)
+ status = inode->i_op->readpage(inode, page);
+ else
+ status = -EIO; /* two tries ... error out */
if (status < 0)
- break;
+ goto done_with_page;
didread++;
- goto lockit;
+ goto page_wait;
}
set_bit(PG_uptodate, &page->flags);
}
- didread = 0;
- /* Alright, the page is there, and we've locked it. Now
- * update it. */
+ /* Alright, the page is there. Now update it. */
status = inode->i_op->updatepage(inode, page, buf,
offset, bytes, sync);
- free_page(page_address(page));
+done_with_page:
+ __free_page(page);
if (status < 0)
break;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov