head	1.1;
access;
symbols
	LKML-branch:1.1.0.2;
locks; strict;
comment	@# @;


1.1
date	2007.01.13.06.16.16;	author jsipek;	state dead;
branches
	1.1.2.1;
next	;

1.1.2.1
date	2007.01.13.06.16.16;	author jsipek;	state Exp;
branches;
next	1.1.2.2;

1.1.2.2
date	2007.02.04.03.35.16;	author jsipek;	state Exp;
branches;
next	;


desc
@@


1.1
log
@file mmap was initially added on branch LKML-branch.
@
text
@@


1.1.2.1
log
@Most of akpm's comments & work-in-progress mmap implementation
@
text
@a0 628
diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile
index 25dd78f..8cdccba 100644
--- a/fs/unionfs/Makefile
+++ b/fs/unionfs/Makefile
@@@@ -2,4 +2,5 @@@@ obj-$(CONFIG_UNION_FS) += unionfs.o
 
 unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \
 	stale_inode.o branchman.o rdstate.o copyup.o dirhelper.o \
-	rename.o unlink.o lookup.o commonfops.o dirfops.o sioq.o
+	rename.o unlink.o lookup.o commonfops.o dirfops.o sioq.o \
+	mmap.o
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index ad70b24..1be851b 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@@@ -206,8 +206,12 @@@@ static inline int __copyup_reg_data(struct dentry *dentry,
 
 	kfree(buf);
 
+	if (!err)
+		err = output_file->f_op->fsync(output_file, new_hidden_dentry, 0);
+
 	if (err)
 		goto out_close_out;
+
 	if (copyup_file) {
 		*copyup_file = output_file;
 		goto out_close_in;
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index 9d05806..06096c2 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@@@ -27,98 +27,21 @@@@ extern ssize_t unionfs_write(struct file *, const char __user *, size_t,
  * File Operations *
  *******************/
 
-static loff_t unionfs_llseek(struct file *file, loff_t offset, int origin)
-{
-	loff_t err;
-	struct file *hidden_file = NULL;
-
-	if ((err = unionfs_file_revalidate(file, 0)))
-		goto out;
-
-	hidden_file = unionfs_lower_file(file);
-	/* always set hidden position to this one */
-	hidden_file->f_pos = file->f_pos;
-
-	memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(struct file_ra_state));
-
-	if (hidden_file->f_op && hidden_file->f_op->llseek)
-		err = hidden_file->f_op->llseek(hidden_file, offset, origin);
-	else
-		err = generic_file_llseek(hidden_file, offset, origin);
-
-	if (err < 0)
-		goto out;
-	if (err != file->f_pos) {
-		file->f_pos = err;
-		file->f_version++;
-	}
-out:
-	return err;
-}
-
 ssize_t unionfs_read(struct file * file, char __user * buf, size_t count,
 		     loff_t * ppos)
 {
-	struct file *hidden_file;
-	loff_t pos = *ppos;
 	int err;
 
 	if ((err = unionfs_file_revalidate(file, 0)))
 		goto out;
 
-	err = -EINVAL;
-	hidden_file = unionfs_lower_file(file);
-	if (!hidden_file->f_op || !hidden_file->f_op->read)
-		goto out;
-
-	err = hidden_file->f_op->read(hidden_file, buf, count, &pos);
-	*ppos = pos;
+	err = do_sync_read(file, buf, count, ppos);
 
-out:
-	return err;
-}
-
-ssize_t __unionfs_write(struct file * file, const char __user * buf,
-			size_t count, loff_t * ppos)
-{
-	int err = -EINVAL;
-	struct file *hidden_file = NULL;
-	struct inode *inode;
-	struct inode *hidden_inode;
-	loff_t pos = *ppos;
-	int bstart, bend;
-
-	inode = file->f_dentry->d_inode;
-
-	bstart = fbstart(file);
-	bend = fbend(file);
-
-	BUG_ON(bstart == -1);
-
-	hidden_file = unionfs_lower_file(file);
-	hidden_inode = hidden_file->f_dentry->d_inode;
-
-	if (!hidden_file->f_op || !hidden_file->f_op->write)
-		goto out;
-
-	/* adjust for append -- seek to the end of the file */
-	if (file->f_flags & O_APPEND)
-		pos = inode->i_size;
-
-	err = hidden_file->f_op->write(hidden_file, buf, count, &pos);
-
-	/*
-	 * copy ctime and mtime from lower layer attributes
-	 * atime is unchanged for both layers
-	 */
+	/* FIXME: why? */
 	if (err >= 0)
-		fsstack_copy_attr_times(inode, hidden_inode);
-
-	*ppos = pos;
+		touch_atime(unionfs_lower_mnt(file->f_path.dentry),
+			    unionfs_lower_dentry(file->f_path.dentry));
 
-	/* update this inode's size */
-	if (pos > inode->i_size)
-		inode->i_size = pos;
 out:
 	return err;
 }
@@@@ -131,7 +54,7 @@@@ ssize_t unionfs_write(struct file * file, const char __user * buf, size_t count,
 	if ((err = unionfs_file_revalidate(file, 1)))
 		goto out;
 
-	err = __unionfs_write(file, buf, count, ppos);
+	err = do_sync_write(file, buf, count, ppos);
 
 out:
 	return err;
@@@@ -143,47 +66,6 @@@@ static int unionfs_file_readdir(struct file *file, void *dirent,
 	return -ENOTDIR;
 }
 
-static unsigned int unionfs_poll(struct file *file, poll_table * wait)
-{
-	unsigned int mask = DEFAULT_POLLMASK;
-	struct file *hidden_file = NULL;
-
-	if (unionfs_file_revalidate(file, 0)) {
-		/* We should pretend an error happend. */
-		mask = POLLERR | POLLIN | POLLOUT;
-		goto out;
-	}
-
-	hidden_file = unionfs_lower_file(file);
-
-	if (!hidden_file->f_op || !hidden_file->f_op->poll)
-		goto out;
-
-	mask = hidden_file->f_op->poll(hidden_file, wait);
-
-out:
-	return mask;
-}
-
-static int __do_mmap(struct file *file, struct vm_area_struct *vma)
-{
-	int err;
-	struct file *hidden_file;
-
-	hidden_file = unionfs_lower_file(file);
-
-	err = -ENODEV;
-	if (!hidden_file->f_op || !hidden_file->f_op->mmap)
-		goto out;
-
-	vma->vm_file = hidden_file;
-	err = hidden_file->f_op->mmap(hidden_file, vma);
-	get_file(hidden_file);	/* make sure it doesn't get freed on us */
-	fput(file);		/* no need to keep extra ref on ours */
-out:
-	return err;
-}
-
 static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	int err = 0;
@@@@ -194,64 +76,24 @@@@ static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)
 	if ((err = unionfs_file_revalidate(file, willwrite)))
 		goto out;
 
-	err = __do_mmap(file, vma);
-
-out:
-	return err;
-}
-
-static int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync)
-{
-	int err;
-	struct file *hidden_file = NULL;
-
-	if ((err = unionfs_file_revalidate(file, 1)))
-		goto out;
-
-	hidden_file = unionfs_lower_file(file);
-
-	err = -EINVAL;
-	if (!hidden_file->f_op || !hidden_file->f_op->fsync)
-		goto out;
-
-	mutex_lock(&hidden_file->f_dentry->d_inode->i_mutex);
-	err = hidden_file->f_op->fsync(hidden_file, hidden_file->f_dentry,
-				       datasync);
-	mutex_unlock(&hidden_file->f_dentry->d_inode->i_mutex);
-
-out:
-	return err;
-}
-
-static int unionfs_fasync(int fd, struct file *file, int flag)
-{
-	int err = 0;
-	struct file *hidden_file = NULL;
-
-	if ((err = unionfs_file_revalidate(file, 1)))
-		goto out;
-
-	hidden_file = unionfs_lower_file(file);
-
-	if (hidden_file->f_op && hidden_file->f_op->fasync)
-		err = hidden_file->f_op->fasync(fd, hidden_file, flag);
+	err = generic_file_mmap(file, vma);
+	if (!err)
+		printk("mmap: generic_file_mmap failed %d\n", err);
 
 out:
 	return err;
 }
 
 struct file_operations unionfs_main_fops = {
-	.llseek		= unionfs_llseek,
+	.llseek		= generic_file_llseek,
 	.read		= unionfs_read,
 	.write		= unionfs_write,
 	.readdir	= unionfs_file_readdir,
-	.poll		= unionfs_poll,
 	.unlocked_ioctl	= unionfs_ioctl,
 	.mmap		= unionfs_mmap,
 	.open		= unionfs_open,
 	.flush		= unionfs_flush,
 	.release	= unionfs_file_release,
-	.fsync		= unionfs_fsync,
-	.fasync		= unionfs_fasync,
+	.fsync		= file_fsync,
 };
 
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 1bbef1c..a5d2457 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@@@ -900,6 +900,14 @@@@ static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
 		break;
 	}
 
+	if (ia->ia_valid & ATTR_SIZE) {
+		if (ia->ia_size != i_size_read(inode)) {
+			err = vmtruncate(inode, ia->ia_size);
+			if (err)
+				printk("unionfs_setattr: vmtruncate failed\n");
+		}
+	}
+
 	/* get the size from the first hidden inode */
 	hidden_inode = unionfs_lower_inode(dentry->d_inode);
 	fsstack_copy_attr_all(inode, hidden_inode, unionfs_get_nlinks);
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index 9cab716..e03bf36 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@@@ -119,11 +119,6 @@@@ int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
 	    S_ISFIFO(hidden_inode->i_mode) || S_ISSOCK(hidden_inode->i_mode))
 		init_special_inode(inode, hidden_inode->i_mode,
 				   hidden_inode->i_rdev);
-	/* Fix our inode's address operations to that of the lower inode
-	 * (Unionfs is FiST-Lite)
-	 */
-	if (inode->i_mapping->a_ops != hidden_inode->i_mapping->a_ops)
-		inode->i_mapping->a_ops = hidden_inode->i_mapping->a_ops;
 
 	/* all well, copy inode attributes */
 	fsstack_copy_attr_all(inode, hidden_inode, unionfs_get_nlinks);
diff --git a/fs/unionfs/mmap.c b/fs/unionfs/mmap.c
new file mode 100644
index 0000000..f4dfda8
--- /dev/null
+++ b/fs/unionfs/mmap.c
@@@@ -0,0 +1,280 @@@@
+#include "union.h"
+
+int unionfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	int err = -EIO;
+	struct inode *inode;
+	struct inode *lower_inode;
+	struct page *lower_page;
+	char *kaddr, *lower_kaddr;
+
+	inode = page->mapping->host;
+	lower_inode = unionfs_lower_inode(inode);
+
+	/* find lower page (returns a locked page) */
+	lower_page = grab_cache_page(lower_inode->i_mapping, page->index);
+	if (!lower_page)
+		goto out;
+
+	/* get page address, and encode it */
+	kaddr = kmap(page);
+	lower_kaddr = kmap(lower_page);
+
+	memcpy(lower_kaddr, kaddr, PAGE_CACHE_SIZE);
+
+	kunmap(page);
+	kunmap(lower_page);
+
+	/* call lower writepage (expects locked page) */
+	err = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
+
+	/*
+	 * update mtime and ctime of lower level file system
+	 * unionfs' mtime and ctime are updated by generic_file_write
+	 */
+	lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+
+	page_cache_release(lower_page);	/* b/c grab_cache_page increased refcnt */
+
+	if (err)
+		ClearPageUptodate(page);
+	else
+		SetPageUptodate(page);
+      out:
+	unlock_page(page);
+
+	return err;
+}
+
+/*
+ * readpage is called from generic_page_read and the fault handler.
+ * If your file system uses generic_page_read for the read op, it
+ * must implement readpage.
+ *
+ * Readpage expects a locked page, and must unlock it.
+ */
+int unionfs_do_readpage(struct file *file, struct page *page)
+{
+	int err = -EIO;
+	struct dentry *dentry;
+	struct file *lower_file = NULL;
+	struct inode *inode, *lower_inode;
+	char *page_data;
+	struct page *lower_page;
+	char *lower_page_data;
+
+	dentry = file->f_dentry;
+	if (UNIONFS_F(file) == NULL) {
+		err = -ENOENT;
+		goto out_err;
+	}
+	lower_file = unionfs_lower_file(file);
+	inode = dentry->d_inode;
+	lower_inode = unionfs_lower_inode(inode);
+
+	lower_page = NULL;
+
+	/* find lower page (returns a locked page) */
+	lower_page = read_cache_page(lower_inode->i_mapping,
+				     page->index,
+				     (filler_t *) lower_inode->i_mapping->
+				     a_ops->readpage, (void *)lower_file);
+
+	if (IS_ERR(lower_page)) {
+		err = PTR_ERR(lower_page);
+		lower_page = NULL;
+		goto out_release;
+	}
+
+	/*
+	 * wait for the page data to show up
+	 * (signaled by readpage as unlocking the page)
+	 */
+	wait_on_page_locked(lower_page);
+	if (!PageUptodate(lower_page)) {
+		/*
+		 * call readpage() again if we returned from wait_on_page with a
+		 * page that's not up-to-date; that can happen when a partial
+		 * page has a few buffers which are ok, but not the whole
+		 * page.
+		 */
+		lock_page(lower_page);
+		err = lower_inode->i_mapping->a_ops->readpage(lower_file,
+							      lower_page);
+		if (err) {
+			lower_page = NULL;
+			goto out_release;
+		}
+		wait_on_page_locked(lower_page);
+		if (!PageUptodate(lower_page)) {
+			err = -EIO;
+			goto out_release;
+		}
+	}
+
+	/* map pages, get their addresses */
+	page_data = (char *)kmap(page);
+	lower_page_data = (char *)kmap(lower_page);
+
+	memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE);
+
+	err = 0;
+
+	kunmap(lower_page);
+	kunmap(page);
+
+      out_release:
+	if (lower_page)
+		page_cache_release(lower_page);	/* undo read_cache_page */
+
+	if (err == 0)
+		SetPageUptodate(page);
+	else
+		ClearPageUptodate(page);
+
+      out_err:
+
+	return err;
+}
+
+int unionfs_readpage(struct file *file, struct page *page)
+{
+	int err;
+
+	err = unionfs_do_readpage(file, page);
+
+	/*
+	 * we have to unlock our page, b/c we _might_ have gotten a locked page.
+	 * but we no longer have to wakeup on our page here, b/c UnlockPage does
+	 * it
+	 */
+
+	unlock_page(page);
+
+	return err;
+}
+
+int unionfs_prepare_write(struct file *file, struct page *page, unsigned from,
+			  unsigned to)
+{
+	return 0;
+}
+
+int unionfs_commit_write(struct file *file, struct page *page, unsigned from,
+			 unsigned to)
+{
+	int err = -ENOMEM;
+	struct inode *inode, *lower_inode;
+	struct file *lower_file = NULL;
+	loff_t pos;
+	unsigned bytes = to - from;
+	char *page_data = NULL;
+	mm_segment_t old_fs;
+
+	BUG_ON(file == NULL);
+
+	inode = page->mapping->host;	/* CPW: Moved below print_entry_location */
+	lower_inode = unionfs_lower_inode(inode);
+
+	if (UNIONFS_F(file) != NULL)
+		lower_file = unionfs_lower_file(file);
+
+	BUG_ON(lower_file == NULL);	/* XXX: is this assertion right here? */
+
+	page_data = (char *)kmap(page);
+	lower_file->f_pos = (page->index << PAGE_CACHE_SHIFT) + from;
+
+	/* SP: I use vfs_write instead of copying page data and the
+	 * prepare_write/commit_write combo because file system's like
+	 * GFS/OCFS2 don't like things touching those directly,
+	 * calling the underlying write op, while a little bit slower, will
+	 * call all the FS specific code as well
+	 */
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	err =
+	    vfs_write(lower_file, page_data + from, bytes, &lower_file->f_pos);
+	set_fs(old_fs);
+
+	kunmap(page);
+
+	if (err < 0)
+		goto out;
+
+	inode->i_blocks = lower_inode->i_blocks;
+	/* we may have to update i_size */
+	pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to;
+	if (pos > i_size_read(inode))
+		i_size_write(inode, pos);
+
+	/*
+	 * update mtime and ctime of lower level file system
+	 * unionfs' mtime and ctime are updated by generic_file_write
+	 */
+	lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+
+	mark_inode_dirty_sync(inode);
+
+      out:
+	if (err < 0)
+		ClearPageUptodate(page);
+
+	return err;		/* assume all is ok */
+}
+
+sector_t unionfs_bmap(struct address_space * mapping, sector_t block)
+{
+	int err = 0;
+	struct inode *inode, *lower_inode;
+
+	inode = (struct inode *)mapping->host;
+	lower_inode = unionfs_lower_inode(inode);
+
+	if (lower_inode->i_mapping->a_ops->bmap)
+		err =
+		    lower_inode->i_mapping->a_ops->bmap(lower_inode->i_mapping,
+							block);
+	return err;
+}
+
+void unionfs_sync_page(struct page *page)
+{
+	struct inode *inode;
+	struct inode *lower_inode;
+	struct page *lower_page;
+	struct address_space *mapping = page->mapping;
+
+	inode = page->mapping->host;	/* CPW: Moved below print_entry_location */
+	lower_inode = unionfs_lower_inode(inode);
+
+	/* find lower page (returns a locked page) */
+	lower_page = grab_cache_page(lower_inode->i_mapping, page->index);
+	if (!lower_page)
+		goto out;
+
+	/* do the actual sync */
+	if (mapping && mapping->a_ops && mapping->a_ops->sync_page)
+		mapping->a_ops->sync_page(page);
+
+	unlock_page(lower_page);	/* b/c grab_cache_page locked it */
+	page_cache_release(lower_page);	/* b/c grab_cache_page increased refcnt */
+
+      out:
+
+	return;
+}
+
+struct address_space_operations unionfs_aops = {
+      writepage:unionfs_writepage,
+      readpage:unionfs_readpage,
+      prepare_write:unionfs_prepare_write,
+      commit_write:unionfs_commit_write,
+      bmap:unionfs_bmap,
+      sync_page:unionfs_sync_page,
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index 25085bf..c6238f3 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@@@ -25,7 +25,7 @@@@ static struct kmem_cache *unionfs_inode_cachep;
 
 static void unionfs_read_inode(struct inode *inode)
 {
-	static struct address_space_operations unionfs_empty_aops;
+	extern struct address_space_operations unionfs_aops;
 	int size;
 	struct unionfs_inode_info *info = UNIONFS_I(inode);
 
@@@@ -57,8 +57,7 @@@@ static void unionfs_read_inode(struct inode *inode)
 	inode->i_op = &unionfs_main_iops;
 	inode->i_fop = &unionfs_main_fops;
 
-	/* I don't think ->a_ops is ever allowed to be NULL */
-	inode->i_mapping->a_ops = &unionfs_empty_aops;
+	inode->i_mapping->a_ops = &unionfs_aops;
 }
 
 static void unionfs_put_inode(struct inode *inode)
@@@@ -89,6 +88,9 @@@@ static void unionfs_delete_inode(struct inode *inode)
 {
 	inode->i_size = 0;	/* every f/s seems to do that */
 
+	if (inode->i_data.nrpages)
+		truncate_inode_pages(&inode->i_data, 0);
+
 	clear_inode(inode);
 }
 
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 624132b..06fa96c 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@@@ -38,6 +38,7 @@@@
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <linux/writeback.h>
+#include <linux/buffer_head.h>
 #include <linux/fs_stack.h>
 #include <linux/magic.h>
 
@


1.1.2.2
log
@Move rebase mmap code on top of xattr enabled code & replace hand coded
round to the next power of two code with a call to __roundup_pow_of_two
@
text
@a0 6
fs/unionfs/: Basic mmap support

Uses memcpy to copy data from lower to upper (and vice versa) pages.

Signed-off-by: Josef 'Jeff' Sipek <jsipek@@cs.sunysb.edu>

d2 1
a2 1
index e6b2e0c..0da2074 100644
d5 1
a5 1
@@@@ -2,6 +2,6 @@@@ obj-$(CONFIG_UNION_FS) += unionfs.o
d8 4
a11 5
 	branchman.o rdstate.o copyup.o dirhelper.o rename.o \
-	unlink.o lookup.o commonfops.o dirfops.o sioq.o
+	unlink.o lookup.o commonfops.o dirfops.o sioq.o mmap.o
 
 unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o
d13 1
a13 1
index 998cc69..8ed5cc3 100644
d16 1
a16 1
@@@@ -284,8 +284,12 @@@@ static int __copyup_reg_data(struct dentry *dentry,
d30 1
a30 1
index 9ce092d..81f2818 100644
d33 1
a33 1
@@@@ -22,98 +22,22 @@@@
d66 2
a67 2
 static ssize_t unionfs_read(struct file * file, char __user * buf,
 			    size_t count, loff_t * ppos)
d83 2
a84 1
-
d89 2
a90 2
-static ssize_t __unionfs_write(struct file * file, const char __user * buf,
-			       size_t count, loff_t * ppos)
d115 1
a115 1
 
d117 1
a117 2
+	err = do_sync_read(unionfs_lower_file(file), buf, count ppos);
 
d136 1
a136 1
@@@@ -126,7 +50,7 @@@@ static ssize_t unionfs_write(struct file * file, const char __user * buf,
d145 1
a145 1
@@@@ -138,47 +62,6 @@@@ static int unionfs_file_readdir(struct file *file, void *dirent,
d193 1
a193 1
@@@@ -189,64 +72,24 @@@@ static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)
d264 1
a264 1
index 1b2e8a8..6a20620 100644
d267 1
a267 1
@@@@ -905,6 +905,14 @@@@ static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
d283 1
a283 1
index 36d30bc..14a5341 100644
d585 1
a585 1
index 38443c7..3ef7e3c 100644
d618 1
a618 1
index fc87b03..73bb4dd 100644
a625 1
 #include <linux/xattr.h>
d628 1
@


