patch-2.2.13 linux/mm/filemap.c
Next file: linux/mm/mmap.c
Previous file: linux/kernel/time.c
Back to the patch index
Back to the overall index
- Lines: 75
- Date:
Tue Oct 19 17:14:02 1999
- Orig file:
v2.2.12/linux/mm/filemap.c
- Orig date:
Tue May 11 08:51:13 1999
diff -u --recursive --new-file v2.2.12/linux/mm/filemap.c linux/mm/filemap.c
@@ -215,8 +215,25 @@
/*
* Update a page cache copy, when we're doing a "write()" system call
* See also "update_vm_cache()".
+ *
+ * This function is conditional in that it checks whether the original
+ * source of the data is the same as the ultimate destination, and
+ * aborts the update if so.
+ *
+ * The "source_address" is the virtual address of the original location
+ * of the data we are injecting. For writes from user mode, it is the
+ * user VA. However, for filemap_sync writes, "source_address", it is
+ * the page cache address. In both cases, "buf" points to the copy we
+ * have already made in kernel space and we use that pointer for the
+ * transfer. source_address just allows us to detect an update_vm_cache
+ * which is being sourced from the copy of the data already in the page
+ * cache.
+ *
+ * This prevents munmap() and msync() from stomping all over shared
+ * memory maps. --sct
*/
-void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf, int count)
+
+void update_vm_cache_conditional(struct inode * inode, unsigned long pos, const char * buf, int count, unsigned long source_address)
{
unsigned long offset, len;
@@ -230,8 +247,12 @@
len = count;
page = find_page(inode, pos);
if (page) {
- wait_on_page(page);
- memcpy((void *) (offset + page_address(page)), buf, len);
+ char *dest = (char*) (offset + page_address(page));
+
+ if ((unsigned long)dest != source_address) {
+ wait_on_page(page);
+ memcpy(dest, buf, len);
+ }
page_cache_release(page);
}
count -= len;
@@ -242,6 +263,12 @@
} while (count);
}
+void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf, int count)
+{
+ update_vm_cache_conditional(inode, pos, buf, count, 0);
+}
+
+
static inline void add_to_page_cache(struct page * page,
struct inode * inode, unsigned long offset,
struct page **hash)
@@ -1482,6 +1509,8 @@
while (count) {
unsigned long bytes, pgpos, offset;
+ char * dest;
+
/*
* Try to find the page in the cache. If it isn't there,
* allocate a free page.
@@ -1516,7 +1545,9 @@
* the writer needs to increment the page use counts until he
* is done with the page.
*/
- bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
+ dest = (char *) page_address(page) + offset;
+ if (dest != buf) /* See comment in update_vm_cache_cond. */
+ bytes -= copy_from_user(dest, buf, bytes);
status = -EFAULT;
if (bytes)
status = inode->i_op->updatepage(file, page, offset, bytes, sync);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)