patch-2.1.31 linux/mm/filemap.c
Next file: linux/net/802/fddi.c
Previous file: linux/kernel/time.c
Back to the patch index
Back to the overall index
- Lines: 119
- Date:
Wed Apr 2 17:43:23 1997
- Orig file:
v2.1.30/linux/mm/filemap.c
- Orig date:
Thu Mar 27 14:40:12 1997
diff -u --recursive --new-file v2.1.30/linux/mm/filemap.c linux/mm/filemap.c
@@ -1275,3 +1275,118 @@
unlock_kernel();
return error;
}
+
+/*
+ * Write to a file through the page cache. This is mainly for the
+ * benefit of NFS and possibly other network-based file systems.
+ *
+ * We currently put everything into the page cache prior to writing it.
+ * This is not a problem when writing full pages. With partial pages,
+ * however, we first have to read the data into the cache, then
+ * dirty the page, and finally schedule it for writing. Alternatively, we
+ * could write-through just the portion of data that would go into that
+ * page, but that would kill performance for applications that write data
+ * line by line, and it's prone to race conditions.
+ *
+ * Note that this routine doesn't try to keep track of dirty pages. Each
+ * file system has to do this all by itself, unfortunately.
+ * okir@monad.swb.de
+ */
+long
+generic_file_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+{
+ struct page *page, **hash;
+ unsigned long page_cache = 0;
+ unsigned long ppos, offset;
+ unsigned int bytes, written;
+ unsigned long pos;
+ int status, sync, didread = 0;
+
+ if (!inode->i_op || !inode->i_op->updatepage)
+ return -EIO;
+
+ sync = file->f_flags & O_SYNC;
+ pos = file->f_pos;
+ written = 0;
+ status = 0;
+
+ if (file->f_flags & O_APPEND)
+ pos = inode->i_size;
+
+ while (count) {
+ /*
+ * Try to find the page in the cache. If it isn't there,
+ * allocate a free page.
+ */
+ offset = (pos & ~PAGE_MASK);
+ ppos = pos & PAGE_MASK;
+
+ if ((bytes = PAGE_SIZE - offset) > count)
+ bytes = count;
+
+ hash = page_hash(inode, ppos);
+ if (!(page = __find_page(inode, ppos, *hash))) {
+ if (!page_cache) {
+ page_cache = __get_free_page(GFP_KERNEL);
+ if (!page_cache) {
+ status = -ENOMEM;
+ break;
+ }
+ continue;
+ }
+ page = mem_map + MAP_NR(page_cache);
+ add_to_page_cache(page, inode, ppos, hash);
+ page_cache = 0;
+ }
+
+lockit:
+ while (set_bit(PG_locked, &page->flags))
+ wait_on_page(page);
+
+ /*
+ * If the page is not uptodate, and we're writing less
+ * than a full page of data, we may have to read it first.
+ * However, don't bother with reading the page when it's
+ * 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 (status < 0)
+ break;
+ didread++;
+ goto lockit;
+ }
+ set_bit(PG_uptodate, &page->flags);
+ }
+ didread = 0;
+
+ /* Alright, the page is there, and we've locked it. Now
+ * update it. */
+ status = inode->i_op->updatepage(inode, page, buf,
+ offset, bytes, sync);
+ free_page(page_address(page));
+ if (status < 0)
+ break;
+
+ written += status;
+ count -= status;
+ pos += status;
+ buf += status;
+ }
+ file->f_pos = pos;
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+
+ if (page_cache)
+ free_page(page_cache);
+ if (written)
+ return written;
+ return status;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov