Re: [Ecryptfs-devel] [PATCH 3/11] eCryptfs: read_write.c routines
On Fri, Sep 21, 2007 at 03:05:40PM -0700, Andrew Morton wrote: > btw, I'm not really a great admirer of the whole patchset: it does > some pretty nasty-looking things: allocating dynamic memory, > grabbing the underlying pageframes with virt_to_page(), passing them > back into kernel APIs which are supposed to be called from > userspace, etc. It's all rather ugly and abusive-looking. Functions higher up the execution stack should be the ones mucking with the Uptodate flag. The patch below addresses some of these issues. I also whipped up a post-patch partial call graph to help illustrate what is going on with the page mapping and Uptodate status in the various eCryptfs read/write paths: http://ecryptfs.sourceforge.net/ecryptfs-pageuptodate-call-graph.png --- The functions that eventually call down to ecryptfs_read_lower(), ecryptfs_decrypt_page(), and ecryptfs_copy_up_encrypted_with_header() should have the responsibility of managing the page Uptodate status. This patch gets rid of some of the ugliness that resulted from trying to push some of the page flag setting too far down the stack. Signed-off-by: Michael Halcrow <[EMAIL PROTECTED]> --- diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index b3795f6..bbec711 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -605,14 +605,14 @@ int ecryptfs_decrypt_page(struct page *page) printk(KERN_ERR "%s: Error attempting to copy " "page at index [%ld]\n", __FUNCTION__, page->index); - goto out_clear_uptodate; + goto out; } enc_extent_virt = kmalloc(PAGE_CACHE_SIZE, GFP_USER); if (!enc_extent_virt) { rc = -ENOMEM; ecryptfs_printk(KERN_ERR, "Error allocating memory for " "encrypted extent\n"); - goto out_clear_uptodate; + goto out; } enc_extent_page = virt_to_page(enc_extent_virt); for (extent_offset = 0; @@ -631,21 +631,17 @@ int ecryptfs_decrypt_page(struct page *page) ecryptfs_printk(KERN_ERR, "Error attempting " "to read lower page; rc = [%d]" "\n", rc); - goto out_clear_uptodate; + goto out; } rc = ecryptfs_decrypt_extent(page, crypt_stat, enc_extent_page, extent_offset); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " "rc = [%d]\n", __FUNCTION__, rc); - goto out_clear_uptodate; + goto out; } extent_offset++; } - SetPageUptodate(page); - goto out; -out_clear_uptodate: - ClearPageUptodate(page); out: kfree(enc_extent_virt); return rc; diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index bb92b74..ce7a5d4 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -648,6 +648,6 @@ int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs, struct inode *ecryptfs_inode); int ecryptfs_read(char *data, loff_t offset, size_t size, struct file *ecryptfs_file); -struct page *ecryptfs_get1page(struct file *file, loff_t index); +struct page *ecryptfs_get_locked_page(struct file *file, loff_t index); #endif /* #ifndef ECRYPTFS_KERNEL_H */ diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index 4eb09c1..16a7a55 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -37,23 +37,27 @@ struct kmem_cache *ecryptfs_lower_page_cache; /** - * ecryptfs_get1page + * ecryptfs_get_locked_page * * Get one page from cache or lower f/s, return error otherwise. * - * Returns unlocked and up-to-date page (if ok), with increased + * Returns locked and up-to-date page (if ok), with increased * refcnt. */ -struct page *ecryptfs_get1page(struct file *file, loff_t index) +struct page *ecryptfs_get_locked_page(struct file *file, loff_t index) { struct dentry *dentry; struct inode *inode; struct address_space *mapping; + struct page *page; dentry = file->f_path.dentry; inode = dentry->d_inode; mapping = inode->i_mapping; - return read_mapping_page(mapping, index, (void *)file); + page = read_mapping_page(mapping, index, (void *)file); + if (!IS_ERR(page)) + lock_page(page); + return page; } /** @@ -146,12 +150,10 @@ ecryptfs_copy_up_encrypted_with_header(struct page *page, kunmap_atomic(page_virt, KM_USER0); flush_dcache_page(page); if (rc) { - ClearPageUptodate(page);
Re: [Ecryptfs-devel] [PATCH 3/11] eCryptfs: read_write.c routines
On Fri, 21 Sep 2007 16:51:25 -0500 Michael Halcrow <[EMAIL PROTECTED]> wrote: > On Wed, Sep 19, 2007 at 10:38:50PM -0700, Andrew Morton wrote: > > > + virt = kmap(page_for_lower); > > > + rc = ecryptfs_write_lower(ecryptfs_inode, virt, offset, size); > > > + kunmap(page_for_lower); > > > + return rc; > > > +} > > > > argh, kmap. http://lkml.org/lkml/2007/9/15/55 > > Here is a patch that moves to kmap_atomic(), adding an intermediate > copy. Although I would really like to find a way to avoid having to do > this extra copy. We might as well stick with kmap. I was just having a whine - I don't know what to do about this really, apart from perhaps giving in to reality and making kmap work better. btw, I'm not really a great admirer of the whole patchset: it does some pretty nasty-looking things: allocating dynamic memory, grabbing the underlying pageframes with virt_to_page(), passing them back into kernel APIs which are supposed to be called from userspace, etc. It's all rather ugly and abusive-looking. But given that you're trying to do things which the kernel just isn't set up to do, it isn't immediately obvious what can be done to fix it. Perhaps there are problems whcih I didn't have time to spot, and perhaps there are things which could be done to improve it. But I don't have time to sit down and absorb it all to a sufficient level of detail to be able to suggest anything, and nobody else seems to be interested in reading the patches so whoop, in it all goes. - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [Ecryptfs-devel] [PATCH 3/11] eCryptfs: read_write.c routines
On Wed, Sep 19, 2007 at 10:38:50PM -0700, Andrew Morton wrote: > > + virt = kmap(page_for_lower); > > + rc = ecryptfs_write_lower(ecryptfs_inode, virt, offset, size); > > + kunmap(page_for_lower); > > + return rc; > > +} > > argh, kmap. http://lkml.org/lkml/2007/9/15/55 Here is a patch that moves to kmap_atomic(), adding an intermediate copy. Although I would really like to find a way to avoid having to do this extra copy. --- Replace kmap() with kmap_atomic() for read_write.c routines kmap() can lead to deadlock when multiple tasks attempt to take more than one simultaneously: http://lkml.org/lkml/2007/9/15/55 In order to avoid this possibility, eCryptfs must allocate an intermediate block of memory to use with vfs_read() and vfs_write(), copying the data through this memory region, since kmap_atomic() cannot be held during calls which may block. Signed-off-by: Michael Halcrow <[EMAIL PROTECTED]> --- diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index bb92b74..ce7a5d4 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -648,6 +648,6 @@ int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs, struct inode *ecryptfs_inode); int ecryptfs_read(char *data, loff_t offset, size_t size, struct file *ecryptfs_file); -struct page *ecryptfs_get1page(struct file *file, loff_t index); +struct page *ecryptfs_get_locked_page(struct file *file, loff_t index); #endif /* #ifndef ECRYPTFS_KERNEL_H */ diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index c6a8a33..6abf805 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -37,23 +37,30 @@ struct kmem_cache *ecryptfs_lower_page_cache; /** - * ecryptfs_get1page + * ecryptfs_get_locked_page * * Get one page from cache or lower f/s, return error otherwise. * - * Returns unlocked and up-to-date page (if ok), with increased + * Returns a locked and up-to-date page (if ok), with increased * refcnt. */ -struct page *ecryptfs_get1page(struct file *file, loff_t index) +struct page *ecryptfs_get_locked_page(struct file *file, loff_t index) { struct dentry *dentry; struct inode *inode; struct address_space *mapping; + struct page *page; dentry = file->f_path.dentry; inode = dentry->d_inode; mapping = inode->i_mapping; - return read_mapping_page(mapping, index, (void *)file); + page = read_mapping_page(mapping, index, (void *)file); + if (!IS_ERR(page)) + lock_page(page); + else + printk(KERN_ERR "%s: Error from read_mapping_page()\n", + __FUNCTION__); + return page; } /** diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c index ccd2599..6732a4c 100644 --- a/fs/ecryptfs/read_write.c +++ b/fs/ecryptfs/read_write.c @@ -83,14 +83,24 @@ int ecryptfs_write_lower_page_segment(struct inode *ecryptfs_inode, struct page *page_for_lower, size_t offset_in_page, size_t size) { - char *virt; + char *page_for_lower_virt; + char *tmp_virt; loff_t offset; int rc; - offset = (page_for_lower->index << PAGE_CACHE_SHIFT) + offset_in_page; - virt = kmap(page_for_lower); - rc = ecryptfs_write_lower(ecryptfs_inode, virt, offset, size); - kunmap(page_for_lower); + tmp_virt = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); + if (!tmp_virt) { + rc = -ENOMEM; + goto out; + } + offset = loff_t)page_for_lower->index) << PAGE_CACHE_SHIFT) + + offset_in_page); + page_for_lower_virt = kmap_atomic(page_for_lower, KM_USER0); + memcpy(tmp_virt, page_for_lower_virt, PAGE_CACHE_SIZE); + kunmap_atomic(page_for_lower_virt, KM_USER0); + rc = ecryptfs_write_lower(ecryptfs_inode, tmp_virt, offset, size); + kfree(tmp_virt); +out: return rc; } @@ -140,8 +150,8 @@ int ecryptfs_write(struct file *ecryptfs_file, char *data, loff_t offset, if (num_bytes > total_remaining_zeros) num_bytes = total_remaining_zeros; } - ecryptfs_page = ecryptfs_get1page(ecryptfs_file, - ecryptfs_page_idx); + ecryptfs_page = ecryptfs_get_locked_page(ecryptfs_file, +ecryptfs_page_idx); if (IS_ERR(ecryptfs_page)) { rc = PTR_ERR(ecryptfs_page); printk(KERN_ERR "%s: Error getting page at " @@ -159,6 +169,7 @@ int ecryptfs_write(struct file *ecryptfs_file, char *data, loff_t offset, printk(KERN_ERR "%s: Error decrypting " "page; rc = [%d]\n",