Patchwork [1/3] fs: Create __block_page_mkwrite() helper passing error values back

login
register
mail settings
Submitter Jan Kara
Date May 10, 2011, 10:29 p.m.
Message ID <1305066574-1573-2-git-send-email-jack@suse.cz>
Download mbox | patch
Permalink /patch/95057/
State Superseded
Headers show

Comments

Jan Kara - May 10, 2011, 10:29 p.m.
Create __block_page_mkwrite() helper which does all what block_page_mkwrite()
does except that it passes back errors from __block_write_begin /
block_commit_write calls. This is needed for some filesystems so that they
can detect errors such as ENOSPC and try harder.

CC: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/buffer.c                 |   26 ++++++++++++++++++--------
 include/linux/buffer_head.h |    2 ++
 2 files changed, 20 insertions(+), 8 deletions(-)
Christoph Hellwig - May 17, 2011, 3:09 p.m.
> +	if (unlikely(ret < 0))
>  		unlock_page(page);
> +	else
>  		ret = VM_FAULT_LOCKED;
>  out:
>  	return ret;

Using two different types of return values here seems rather unclean.
I'd rather use 0 instead of VM_FAULT_LOCKED here, and maybe overload
-EAGAIN for VM_FAULT_NOPAGE.

> +int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
> +		   get_block_t get_block)
> +{
> +	int ret = __block_page_mkwrite(vma, vmf, get_block);
> +
> +	if (unlikely(ret < 0)) {
> +		if (ret == -ENOMEM)
> +			return VM_FAULT_OOM;
> +		/* -ENOSPC, -EIO, etc */
> +		return VM_FAULT_SIGBUS;
> +	}
> +	return ret;

and maybe also add a small inlined block_page_mkwrite_error helper
to translate the values.

Alternatively it might make sense to add a VM_FAULT_ENOSPC return value
so that you could re-use block_page_mkwrite unmodified, and we could
also have better error reporting for that case for the core VM.

--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Kara - May 18, 2011, 7:35 a.m.
On Tue 17-05-11 11:09:03, Christoph Hellwig wrote:
> > +	if (unlikely(ret < 0))
> >  		unlock_page(page);
> > +	else
> >  		ret = VM_FAULT_LOCKED;
> >  out:
> >  	return ret;
> 
> Using two different types of return values here seems rather unclean.
> I'd rather use 0 instead of VM_FAULT_LOCKED here, and maybe overload
> -EAGAIN for VM_FAULT_NOPAGE.
  OK, makes sense. I'll do that.

> > +int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
> > +		   get_block_t get_block)
> > +{
> > +	int ret = __block_page_mkwrite(vma, vmf, get_block);
> > +
> > +	if (unlikely(ret < 0)) {
> > +		if (ret == -ENOMEM)
> > +			return VM_FAULT_OOM;
> > +		/* -ENOSPC, -EIO, etc */
> > +		return VM_FAULT_SIGBUS;
> > +	}
> > +	return ret;
> 
> and maybe also add a small inlined block_page_mkwrite_error helper
> to translate the values.
  Good idea.

> Alternatively it might make sense to add a VM_FAULT_ENOSPC return value
> so that you could re-use block_page_mkwrite unmodified, and we could
> also have better error reporting for that case for the core VM.
  Umm, I think the first solution is better. For example ext[34] needs to
differentiate ENOSPC and EDQUOT (for the second one it does not make sense
to force transaction commit because quota accounting is always precise) and
other filesystems might possibly have different requirements. So returning
the error value and letting fs sort it out looks like the most versatile
solution.

								Honza

Patch

diff --git a/fs/buffer.c b/fs/buffer.c
index a08bb8e..469c832 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2333,7 +2333,7 @@  EXPORT_SYMBOL(block_commit_write);
  * unlock the page.
  */
 int
-block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
+__block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
 		   get_block_t get_block)
 {
 	struct page *page = vmf->page;
@@ -2361,18 +2361,28 @@  block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
 	if (!ret)
 		ret = block_commit_write(page, 0, end);
 
-	if (unlikely(ret)) {
+	if (unlikely(ret < 0))
 		unlock_page(page);
-		if (ret == -ENOMEM)
-			ret = VM_FAULT_OOM;
-		else /* -ENOSPC, -EIO, etc */
-			ret = VM_FAULT_SIGBUS;
-	} else
+	else
 		ret = VM_FAULT_LOCKED;
-
 out:
 	return ret;
 }
+EXPORT_SYMBOL(__block_page_mkwrite);
+
+int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
+		   get_block_t get_block)
+{
+	int ret = __block_page_mkwrite(vma, vmf, get_block);
+
+	if (unlikely(ret < 0)) {
+		if (ret == -ENOMEM)
+			return VM_FAULT_OOM;
+		/* -ENOSPC, -EIO, etc */
+		return VM_FAULT_SIGBUS;
+	}
+	return ret;
+}
 EXPORT_SYMBOL(block_page_mkwrite);
 
 /*
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index f5df235..0b719b0 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -217,6 +217,8 @@  int cont_write_begin(struct file *, struct address_space *, loff_t,
 			get_block_t *, loff_t *);
 int generic_cont_expand_simple(struct inode *inode, loff_t size);
 int block_commit_write(struct page *page, unsigned from, unsigned to);
+int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
+				get_block_t get_block);
 int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
 				get_block_t get_block);
 sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);