Skip to contentSkip to conversation

+ fs-introduce-write_begin-write_end-and-perform_write-aops.patch added to -mm tree

PATCH New [email protected] 10 participants 20 messages May 29, 2007 – Jun 16, 2007

Subsystems (4)
lore
Share & tools
lore

[email protected]

author maintainer tree notice
#0 lore raw

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

[[email protected]: API design contributions, code review and fixes]

---

 Documentation/filesystems/Locking |    9 -
 Documentation/filesystems/vfs.txt |   45 +++++
 drivers/block/loop.c              |   75 +++-----
 fs/buffer.c                       |  198 +++++++++++++++++++----
 fs/libfs.c                        |   44 +++++
 fs/namei.c                        |   47 +----
 fs/splice.c                       |   70 --------
 include/linux/buffer_head.h       |   10 +
 include/linux/fs.h                |   30 +++
 include/linux/pagemap.h           |    2 
 mm/filemap.c                      |  238 ++++++++++++++++++++++++----
 11 files changed, 562 insertions(+), 206 deletions(-)
Documentation/filesystems/Locking renamed from Documentation/filesystems/Locking~fs-introduce-write_begin-write_end-and-perform_write-aops +6 -3
diff -puN Documentation/filesystems/Locking~fs-introduce-write_begin-write_end-and-perform_write-aops Documentation/filesystems/Locking
@@ -178,15 +178,18 @@ prototypes:
 locking rules:
 	All except set_page_dirty may block
 
-			BKL	PageLocked(page)
+			BKL	PageLocked(page)	i_sem
 writepage:		no	yes, unlocks (see below)
 readpage:		no	yes, unlocks
 sync_page:		no	maybe
 writepages:		no
 set_page_dirty		no	no
 readpages:		no
-prepare_write:		no	yes
-commit_write:		no	yes
+prepare_write:		no	yes			yes
+commit_write:		no	yes			yes
+write_begin:		no	locks the page		yes
+write_end:		no	yes, unlocks		yes
+perform_write:		no	n/a			yes
 bmap:			yes
 invalidatepage:		no	yes
 releasepage:		no	yes
Documentation/filesystems/vfs.txt renamed from Documentation/filesystems/vfs.txt~fs-introduce-write_begin-write_end-and-perform_write-aops +45 -0

57 lines not on this page · raw message

drivers/block/loop.c renamed from drivers/block/loop.c~fs-introduce-write_begin-write_end-and-perform_write-aops +29 -46

127 lines not on this page · raw message

fs/buffer.c renamed from fs/buffer.c~fs-introduce-write_begin-write_end-and-perform_write-aops +166 -32

222 lines not on this page · raw message

fs/libfs.c renamed from fs/libfs.c~fs-introduce-write_begin-write_end-and-perform_write-aops +44 -0

62 lines not on this page · raw message

fs/namei.c renamed from fs/namei.c~fs-introduce-write_begin-write_end-and-perform_write-aops +12 -35

65 lines not on this page · raw message

fs/splice.c renamed from fs/splice.c~fs-introduce-write_begin-write_end-and-perform_write-aops +8 -62

99 lines not on this page · raw message

include/linux/buffer_head.h renamed from include/linux/buffer_head.h~fs-introduce-write_begin-write_end-and-perform_write-aops +10 -0
diff -puN include/linux/buffer_head.h~fs-introduce-write_begin-write_end-and-perform_write-aops include/linux/buffer_head.h
@@ -203,6 +203,16 @@ void block_invalidatepage(struct page *p
 int block_write_full_page(struct page *page, get_block_t *get_block,
 				struct writeback_control *wbc);
 int block_read_full_page(struct page*, get_block_t*);
+int block_write_begin(struct file *, struct address_space *,
+				loff_t, unsigned, unsigned,
+				struct page **, void **, get_block_t*);
+int block_write_end(struct file *, struct address_space *,
+				loff_t, unsigned, unsigned,
+				struct page *, void *);
+int generic_write_end(struct file *, struct address_space *,
+				loff_t, unsigned, unsigned,
+				struct page *, void *);
+void page_zero_new_buffers(struct page *page, unsigned from, unsigned to);
 int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
 int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
 				loff_t *);
include/linux/fs.h renamed from include/linux/fs.h~fs-introduce-write_begin-write_end-and-perform_write-aops +29 -1
diff -puN include/linux/fs.h~fs-introduce-write_begin-write_end-and-perform_write-aops include/linux/fs.h
@@ -391,6 +391,8 @@ enum positive_aop_returns {
 	AOP_TRUNCATED_PAGE	= 0x80001,
 };
 
+#define AOP_FLAG_UNINTERRUPTIBLE	0x0001 /* will not do a short write */
+
 /*
  * oh the beauties of C type declarations.
  */
@@ -410,7 +412,7 @@ size_t iov_iter_copy_from_user_atomic(st
 size_t iov_iter_copy_from_user(struct page *page,
 		struct iov_iter *i, unsigned long offset, size_t bytes);
 void iov_iter_advance(struct iov_iter *i, size_t bytes);
-int iov_iter_fault_in_readable(struct iov_iter *i);
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
 size_t iov_iter_single_seg_count(struct iov_iter *i);
 
 static inline void iov_iter_init(struct iov_iter *i,
@@ -451,6 +453,14 @@ struct address_space_operations {
 	 */
 	int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
 	int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
+
+	int (*write_begin)(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata);
+	int (*write_end)(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata);
+
 	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
 	sector_t (*bmap)(struct address_space *, sector_t);
 	void (*invalidatepage) (struct page *, unsigned long);
@@ -465,6 +475,18 @@ struct address_space_operations {
 	int (*launder_page) (struct page *);
 };
 
+/*
+ * pagecache_write_begin/pagecache_write_end must be used by general code
+ * to write into the pagecache.
+ */
+int pagecache_write_begin(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata);
+
+int pagecache_write_end(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata);
+
 struct backing_dev_info;
 struct address_space {
 	struct inode		*host;		/* owner: inode, block_device */
@@ -1920,6 +1942,12 @@ extern int simple_prepare_write(struct f
 			unsigned offset, unsigned to);
 extern int simple_commit_write(struct file *file, struct page *page,
 				unsigned offset, unsigned to);
+extern int simple_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata);
+extern int simple_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata);
 
 extern struct dentry *simple_lookup(struct inode *, struct dentry *, struct nameidata *);
 extern ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *);
include/linux/pagemap.h renamed from include/linux/pagemap.h~fs-introduce-write_begin-write_end-and-perform_write-aops +2 -0
diff -puN include/linux/pagemap.h~fs-introduce-write_begin-write_end-and-perform_write-aops include/linux/pagemap.h
@@ -96,6 +96,8 @@ unsigned find_get_pages_contig(struct ad
 unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,
 			int tag, unsigned int nr_pages, struct page **pages);
 
+struct page *__grab_cache_page(struct address_space *mapping, pgoff_t index);
+
 /*
  * Returns locked page at given index in given cache, creating it if needed.
  */
mm/filemap.c renamed from mm/filemap.c~fs-introduce-write_begin-write_end-and-perform_write-aops +211 -27

313 lines not on this page · raw message

_

Patches currently in -mm which might be from [email protected] are

mm-fix-fault-vs-invalidate-race-for-linear-mappings.patch mm-fix-fault-vs-invalidate-race-for-linear-mappings-fix.patch mm-merge-populate-and-nopage-into-fault-fixes-nonlinear.patch mm-merge-nopfn-into-fault.patch
mm-remove-legacy-cruft.patch mm-debug-check-for-the-fault-vs-invalidate-race.patch mm-fix-clear_page_dirty_for_io-vs-fault-race.patch
git-arm-master.patch
slob-rework-freelist-handling.patch
slob-remove-bigblock-tracking.patch slob-improved-alignment-handling.patch mm-revert-kernel_ds-buffered-write-optimisation.patch revert-81b0c8713385ce1b1b9058e916edcf9561ad76d6.patch revert-6527c2bdf1f833cc18e8f42bd97973d583e4aa83.patch mm-clean-up-buffered-write-code.patch
mm-debug-write-deadlocks.patch
mm-trim-more-holes.patch
mm-buffered-write-cleanup.patch
mm-write-iovec-cleanup.patch
mm-fix-pagecache-write-deadlocks.patch
mm-buffered-write-iterator.patch
fs-fix-data-loss-on-error.patch fs-introduce-write_begin-write_end-and-perform_write-aops.patch mm-restore-kernel_ds-optimisations.patch
implement-simple-fs-aops.patch
block_dev-convert-to-new-aops.patch
ext2-convert-to-new-aops.patch
ext3-convert-to-new-aops.patch
ext4-convert-to-new-aops.patch
xfs-convert-to-new-aops.patch
fs-new-cont-helpers.patch
fat-convert-to-new-aops.patch
hfs-convert-to-new-aops.patch
hfsplus-convert-to-new-aops.patch
hpfs-convert-to-new-aops.patch
bfs-convert-to-new-aops.patch
qnx4-convert-to-new-aops.patch
reiserfs-use-generic-write.patch
reiserfs-convert-to-new-aops.patch reiserfs-use-generic_cont_expand_simple.patch with-reiserfs-no-longer-using-the-weird-generic_cont_expand-remove-it-completely.patch nfs-convert-to-new-aops.patch
smb-convert-to-new-aops.patch
fuse-convert-to-new-aops.patch
hostfs-convert-to-new-aops.patch
jffs2-convert-to-new-aops.patch
ufs-convert-to-new-aops.patch
udf-convert-to-new-aops.patch
sysv-convert-to-new-aops.patch
minix-convert-to-new-aops.patch
jfs-convert-to-new-aops.patch
mm-document-fault_data-and-flags.patch fs-introduce-some-page-buffer-invariants.patch fs-introduce-write_begin-write_end-and-perform_write-aops-revoke.patch

#1 lore raw
Quoted 11 lines · Andrew Morton

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

Quoted 7 lines

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

OK, well now Andrew's merged a significant chunk of this work, I would like to try getting the clustered filesystem patches back in too (Steven, the last GFS2 patch you sent had rejects against this tree, so I dropped it... hope it isn't too much work to bring it back uptodate?).

The cluster filesystems aren't 100% happy with the backward-compat code, because pagecache_write_end cannot handle AOP_TRUNCATED_PAGE from ->commit_write... so if you were to try using loop over GFS2, it might go BUG. This is a bit bad of me, however the compat code would have been a whole lot uglier to support that, and I figure the cluster filesystems want to convert to the new aops ASAP anyway.

I doubt anybody but the filesystem developers would be using -mm in such a way, but even so I hope we can fix this before long.

Meanwhile, I'll look at redoing the rest of the filesystems that got left behind.

author maintainer
#2 lore raw
Quoted 33 lines · On Wed, 30 May 2007 05:13:54 +0200 Nick Piggin <[email protected]> wrote:
Quoted 11 lines · Andrew Morton

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

Quoted 7 lines

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

OK, well now Andrew's merged a significant chunk of this work, I would like to try getting the clustered filesystem patches back in too (Steven, the last GFS2 patch you sent had rejects against this tree, so I dropped it... hope it isn't too much work to bring it back uptodate?).

The cluster filesystems aren't 100% happy with the backward-compat code, because pagecache_write_end cannot handle AOP_TRUNCATED_PAGE from ->commit_write... so if you were to try using loop over GFS2, it might go BUG. This is a bit bad of me, however the compat code would have been a whole lot uglier to support that, and I figure the cluster filesystems want to convert to the new aops ASAP anyway.

Quoted 5 lines

I doubt anybody but the filesystem developers would be using -mm in such a way, but even so I hope we can fix this before long.

Meanwhile, I'll look at redoing the rest of the filesystems that got left behind.

hm, I suppose that means I need to undrop git-ocfs2.patch. It has a mild disagreeement with the fault-vs-invalidate patches which I didn't feel like fixing.

#3 lore raw
Quoted 3 lines · Andrew Morton

hm, I suppose that means I need to undrop git-ocfs2.patch. It has a mild disagreeement with the fault-vs-invalidate patches which I didn't feel like fixing.

Erf, somehow I missed that it was dropped. I guess I'll take a look...

	--Mark

--
Mark Fasheh
Senior Software Developer, Oracle
[email protected]

author maintainer
#4 lore raw
Quoted 6 lines · On Wed, 30 May 2007 15:44:43 -0700 Mark Fasheh <[email protected]> wrote:
Quoted 3 lines · Andrew Morton

hm, I suppose that means I need to undrop git-ocfs2.patch. It has a mild disagreeement with the fault-vs-invalidate patches which I didn't feel like fixing.

Erf, somehow I missed that it was dropped.

I didn't tell you ;)

Quoted 1 line

I guess I'll take a look...

It's the sort of mm-vs-mm thing which I get to fix, only I've been putting it off. As it now looks like the fault patches are moing away from death's door, I shall do that.

#5 lore raw
In reply to Nick Piggin

Hi,

Quoted 20 lines · Nick Piggin
Quoted 11 lines · Andrew Morton

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

Quoted 7 lines

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

Quoted 6 lines

OK, well now Andrew's merged a significant chunk of this work, I would like to try getting the clustered filesystem patches back in too (Steven, the last GFS2 patch you sent had rejects against this tree, so I dropped it... hope it isn't too much work to bring it back uptodate?).

I think the following should do the trick... sorry for the delay, I was on holiday last week and I'm just catching up again. There is not a lot of change from the previous version, just a few small changes in the upstream code which had caused one chunk not to apply,

Steve.

---------------------------------------------------------------------------------

fs/gfs2/ops_address.c +128 -84
index fb84478..ad84a55 100644
@@ -17,6 +17,7 @@
 #include <linux/mpage.h>
 #include <linux/fs.h>
 #include <linux/writeback.h>
+#include <linux/swap.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/lm_interface.h>
 
@@ -349,45 +350,49 @@ out_unlock:
 }
 
 /**
- * gfs2_prepare_write - Prepare to write a page to a file
+ * gfs2_write_begin - Begin to write to a file
  * @file: The file to write to
- * @page: The page which is to be prepared for writing
- * @from: From (byte range within page)
- * @to: To (byte range within page)
+ * @mapping: The mapping in which to write
+ * @pos: The file offset at which to start writing
+ * @len: Length of the write
+ * @flags: Various flags
+ * @pagep: Pointer to return the page
+ * @fsdata: Pointer to return fs data (unused by GFS2)
  *
  * Returns: errno
  */
 
-static int gfs2_prepare_write(struct file *file, struct page *page,
-			      unsigned from, unsigned to)
+static int gfs2_write_begin(struct file *file, struct address_space *mapping,
+			    loff_t pos, unsigned len, unsigned flags,
+			    struct page **pagep, void **fsdata)
 {
-	struct gfs2_inode *ip = GFS2_I(page->mapping->host);
-	struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
+	struct gfs2_inode *ip = GFS2_I(mapping->host);
+	struct gfs2_sbd *sdp = GFS2_SB(mapping->host);
 	unsigned int data_blocks, ind_blocks, rblocks;
 	int alloc_required;
 	int error = 0;
-	loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + from;
-	loff_t end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
 	struct gfs2_alloc *al;
-	unsigned int write_len = to - from;
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+	unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+	unsigned to = from + len;
+	struct page *page;
 
-
-	gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_ATIME|LM_FLAG_TRY_1CB, &ip->i_gh);
+	gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_ATIME, &ip->i_gh);
 	error = gfs2_glock_nq_atime(&ip->i_gh);
-	if (unlikely(error)) {
-		if (error == GLR_TRYFAILED) {
-			unlock_page(page);
-			error = AOP_TRUNCATED_PAGE;
-			yield();
-		}
+	if (unlikely(error))
 		goto out_uninit;
-	}
 
-	gfs2_write_calc_reserv(ip, write_len, &data_blocks, &ind_blocks);
+	error = -ENOMEM;
+	page = __grab_cache_page(mapping, index);
+	*pagep = page;
+	if (!page)
+		goto out_unlock;
+
+	gfs2_write_calc_reserv(ip, len, &data_blocks, &ind_blocks);
 
-	error = gfs2_write_alloc_required(ip, pos, write_len, &alloc_required);
+	error = gfs2_write_alloc_required(ip, pos, len, &alloc_required);
 	if (error)
-		goto out_unlock;
+		goto out_putpage;
 
 
 	ip->i_alloc.al_requested = 0;
@@ -419,7 +424,7 @@ static int gfs2_prepare_write(struct file *file, struct page *page,
 		goto out;
 
 	if (gfs2_is_stuffed(ip)) {
-		if (end > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) {
+		if (pos + len > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) {
 			error = gfs2_unstuff_dinode(ip, page);
 			if (error == 0)
 				goto prepare_write;
@@ -441,6 +446,10 @@ out_qunlock:
 out_alloc_put:
 			gfs2_alloc_put(ip);
 		}
+out_putpage:
+		page_cache_release(page);
+		if (pos + len > ip->i_inode.i_size)
+			vmtruncate(&ip->i_inode, ip->i_inode.i_size);
 out_unlock:
 		gfs2_glock_dq_m(1, &ip->i_gh);
 out_uninit:
@@ -476,65 +485,118 @@ static void adjust_fs_space(struct inode *inode)
 }
 
 /**
- * gfs2_commit_write - Commit write to a file
+ * gfs2_stuffed_write_end - Write end for stuffed files
+ * @inode: The inode
+ * @dibh: The buffer_head containing the on-disk inode
+ * @pos: The file position
+ * @len: The length of the write
+ * @copied: How much was actually copied by the VFS
+ * @page: The page
+ *
+ * This copies the data from the page into the inode block after
+ * the inode data structure itself.
+ *
+ * Returns: errno
+ */
+static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
+				  loff_t pos, unsigned len, unsigned copied,
+				  struct page *page)
+{
+	struct gfs2_inode *ip = GFS2_I(inode);
+	struct gfs2_sbd *sdp = GFS2_SB(inode);
+	u64 to = pos + copied;
+	void *kaddr;
+	unsigned char *buf = dibh->b_data + sizeof(struct gfs2_dinode);
+	struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data;
+
+	BUG_ON((pos + len) > (dibh->b_size - sizeof(struct gfs2_dinode)));
+	kaddr = kmap_atomic(page, KM_USER0);
+	memcpy(buf + pos, kaddr + pos, copied);
+	memset(kaddr + pos + copied, 0, len - copied);
+	flush_dcache_page(page);
+	kunmap_atomic(kaddr, KM_USER0);
+
+	if (!PageUptodate(page))
+		SetPageUptodate(page);
+	unlock_page(page);
+	mark_page_accessed(page);
+	page_cache_release(page);
+
+	if (inode->i_size < to) {
+		i_size_write(inode, to);
+		ip->i_di.di_size = inode->i_size;
+		di->di_size = cpu_to_be64(inode->i_size);
+		mark_inode_dirty(inode);
+	}
+
+	if (inode == sdp->sd_rindex)
+		adjust_fs_space(inode);
+
+	brelse(dibh);
+	gfs2_trans_end(sdp);
+	gfs2_glock_dq(&ip->i_gh);
+	gfs2_holder_uninit(&ip->i_gh);
+	return copied;
+}
+
+/**
+ * gfs2_write_end
  * @file: The file to write to
- * @page: The page containing the data
- * @from: From (byte range within page)
- * @to: To (byte range within page)
+ * @mapping: The address space to write to
+ * @pos: The file position
+ * @len: The length of the data
+ * @copied:
+ * @page: The page that has been written
+ * @fsdata: The fsdata (unused in GFS2)
+ *
+ * The main write_end function for GFS2. We have a separate one for
+ * stuffed files as they are slightly different, otherwise we just
+ * put our locking around the VFS provided functions.
  *
  * Returns: errno
  */
 
-static int gfs2_commit_write(struct file *file, struct page *page,
-			     unsigned from, unsigned to)
+static int gfs2_write_end(struct file *file, struct address_space *mapping,
+			  loff_t pos, unsigned len, unsigned copied,
+			  struct page *page, void *fsdata)
 {
 	struct inode *inode = page->mapping->host;
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_sbd *sdp = GFS2_SB(inode);
-	int error = -EOPNOTSUPP;
 	struct buffer_head *dibh;
 	struct gfs2_alloc *al = &ip->i_alloc;
 	struct gfs2_dinode *di;
+	unsigned int from = pos & (PAGE_CACHE_SIZE - 1);
+	unsigned int to = from + len;
+	int ret;
 
-	if (gfs2_assert_withdraw(sdp, gfs2_glock_is_locked_by_me(ip->i_gl)))
-                goto fail_nounlock;
+	BUG_ON(gfs2_glock_is_locked_by_me(ip->i_gl) == 0);
 
-	error = gfs2_meta_inode_buffer(ip, &dibh);
-	if (error)
-		goto fail_endtrans;
+	ret = gfs2_meta_inode_buffer(ip, &dibh);
+	if (unlikely(ret)) {
+		unlock_page(page);
+		page_cache_release(page);
+		goto failed;
+	}
 
 	gfs2_trans_add_bh(ip->i_gl, dibh, 1);
-	di = (struct gfs2_dinode *)dibh->b_data;
 
-	if (gfs2_is_stuffed(ip)) {
-		u64 file_size;
-		void *kaddr;
+	if (gfs2_is_stuffed(ip))
+		return gfs2_stuffed_write_end(inode, dibh, pos, len, copied, page);
 
-		file_size = ((u64)page->index << PAGE_CACHE_SHIFT) + to;
+	if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip))
+		gfs2_page_add_databufs(ip, page, from, to);
 
-		kaddr = kmap_atomic(page, KM_USER0);
-		memcpy(dibh->b_data + sizeof(struct gfs2_dinode) + from,
-		       kaddr + from, to - from);
-		kunmap_atomic(kaddr, KM_USER0);
+	ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
 
-		SetPageUptodate(page);
-
-		if (inode->i_size < file_size) {
-			i_size_write(inode, file_size);
+	if (likely(ret >= 0)) {
+		copied = ret;
+		if  ((pos + copied) > inode->i_size) {
+			di = (struct gfs2_dinode *)dibh->b_data;
+			ip->i_di.di_size = inode->i_size;
+			di->di_size = cpu_to_be64(inode->i_size);
 			mark_inode_dirty(inode);
 		}
-	} else {
-		if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED ||
-		    gfs2_is_jdata(ip))
-			gfs2_page_add_databufs(ip, page, from, to);
-		error = generic_commit_write(file, page, from, to);
-		if (error)
-			goto fail;
-	}
-
-	if (ip->i_di.di_size < inode->i_size) {
-		ip->i_di.di_size = inode->i_size;
-		di->di_size = cpu_to_be64(inode->i_size);
 	}
 
 	if (inode == sdp->sd_rindex)
@@ -542,33 +604,15 @@ static int gfs2_commit_write(struct file *file, struct page *page,
 
 	brelse(dibh);
 	gfs2_trans_end(sdp);
+failed:
 	if (al->al_requested) {
 		gfs2_inplace_release(ip);
 		gfs2_quota_unlock(ip);
 		gfs2_alloc_put(ip);
 	}
-	unlock_page(page);
-	gfs2_glock_dq_m(1, &ip->i_gh);
-	lock_page(page);
+	gfs2_glock_dq(&ip->i_gh);
 	gfs2_holder_uninit(&ip->i_gh);
-	return 0;
-
-fail:
-	brelse(dibh);
-fail_endtrans:
-	gfs2_trans_end(sdp);
-	if (al->al_requested) {
-		gfs2_inplace_release(ip);
-		gfs2_quota_unlock(ip);
-		gfs2_alloc_put(ip);
-	}
-	unlock_page(page);
-	gfs2_glock_dq_m(1, &ip->i_gh);
-	lock_page(page);
-	gfs2_holder_uninit(&ip->i_gh);
-fail_nounlock:
-	ClearPageUptodate(page);
-	return error;
+	return ret;
 }
 
 /**
@@ -837,8 +881,8 @@ const struct address_space_operations gfs2_file_aops = {
 	.readpage = gfs2_readpage,
 	.readpages = gfs2_readpages,
 	.sync_page = block_sync_page,
-	.prepare_write = gfs2_prepare_write,
-	.commit_write = gfs2_commit_write,
+	.write_begin = gfs2_write_begin,
+	.write_end = gfs2_write_end,
 	.bmap = gfs2_bmap,
 	.invalidatepage = gfs2_invalidatepage,
 	.releasepage = gfs2_releasepage,
#6 lore raw
Quoted 29 lines · Steven Whitehouse

Hi,

Quoted 20 lines · Nick Piggin
Quoted 11 lines · Andrew Morton

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

Quoted 7 lines

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

Quoted 6 lines

OK, well now Andrew's merged a significant chunk of this work, I would like to try getting the clustered filesystem patches back in too (Steven, the last GFS2 patch you sent had rejects against this tree, so I dropped it... hope it isn't too much work to bring it back uptodate?).

Quoted 4 lines

I think the following should do the trick... sorry for the delay, I was on holiday last week and I'm just catching up again. There is not a lot of change from the previous version, just a few small changes in the upstream code which had caused one chunk not to apply,

OK thanks! I'll send this to Andrew after he releases the next -mm and hopefully picks up the patchset again.

#7 lore raw
In reply to [email protected]
Quoted 15 lines · On 14:19 Втр 29 Май , [email protected] wrote:

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

Quoted 8 lines

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

[[email protected]: API design contributions, code review and fixes]

I've finaly find time to review Nick's "write_begin/write_end aop" patch set. And i have some fixes and questions. My be i've missed somthing and it was already disscussed, but i cant find in LKML.

1) loop dev:
loop.c code itself is not perfect. In fact before Nick's patch partial write was't possible. Assumption what write chunks are always page aligned is realy weird ( see "index++" line). Fixed by "new aop loop fix" patch

2)block_write_begin:

	After we enter to block_write_begin with *pagep == NULL and
	some page was grabed we remember this page in *pagep
	And if __block_prepare_write() we have to clear *pagep , as 
	it was before. Because this may confuse caller.
	for example caller may have folowing code:
		ret = block_write_begin(..., pagep,...)
		if (ret && *pagep != NULL) {
			unlock_page(*pagep);
			page_cache_release(*pagep);
		}
	Fixed my "new aop block_write_begin fix" patch

3) __page_symlink:
Nick's patch add folowing code:
+ err = pagecache_write_begin(NULL, mapping, 0,PAGE_CACHE_SIZE,
+ AOP_FLAG_UNINTERRUPTIBLE, &page,&fsdata);
symlink now consume whole page. I have only one question "WHY???". I don't see any advantages, but where are huge list of dissdvantages:
a) fs with blksize == 1k and pagesize == 16k after this patch waste more than 10x times disk space for nothing.
b) What happends if we want use fs with blksize == 4k on i386 after it was used by ia64 ??? (before this patch it was possible).

I dont prepare patch for this because i dont understand issue witch Nick aimed to fix.

4) iov_iter_fault_in_readable:
Function prerform check for signgle region, with out respect to segment nature of iovec, For example writev no longer works :) :
writev(3, [{"\1", 1}, {"\2"..., 4096}], 2) = -1 EFAULT (Bad address) this hidden bug, and it was invisiable because XXXX_fault_in_readable return value was ignored before. Lets iov_iter_fault_in_readable perform checks for all segments.
Fixed by :"iov_iter_fault_in_readable fix"

5) ext3_write_end:

	Before  write_begin/write_end patch set we have folowing locking
	order:
		stop_journal(handle);
		unlock_page(page);
	But now order is oposite:
		unlock_page(page);
		stop_journal(handle);
	Can we got any race condition now? I'm not sure is it actual problem,
	may be somebody cant describe this.
#8 lore raw

PATCH 1/1 new aop block_write_begin fix

	After we enter to block_write_begin with *pagep == NULL and

some page was grabed we remember this page in *pagep And if __block_prepare_write() we have to clear *pagep , as it was before. Because this may confuse caller. for example caller may have folowing code:
ret = block_write_begin(..., pagep,...) if (ret && *pagep != NULL) {
unlock_page(*pagep); page_cache_release(*pagep);
}

fs/buffer.c +1 -0
index 07cd457..df933ba 100644
@@ -1982,6 +1982,7 @@ int block_write_begin(struct file *file, struct address_space *mapping,
 		if (ownpage) {
 			unlock_page(page);
 			page_cache_release(page);
+			*pagep = NULL;
 
 			/*
 			 * prepare_write() may have instantiated a few blocks 
#9 lore raw

iov_iter_fault_in_readable fix

In reply to Dmitriy Monakhov

Function prerform check for signgle region, with out respect to segment nature of iovec, For example writev no longer works :)

	/* TESTCASE BEGIN */
	#include <stdio.h>
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <fcntl.h>
	#include <sys/uio.h>
	#include <sys/mman.h>
	#define SIZE  (4096 * 2)
	int main(int argc, char* argv[])
	{	
		char* ptr[4];
		struct iovec iov[2];
		int fd, ret;
		ptr[0] = mmap(NULL, SIZE, PROT_READ|PROT_WRITE,
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
		ptr[1] = mmap(NULL, SIZE, PROT_NONE,
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
		ptr[2] = mmap(NULL, SIZE, PROT_READ|PROT_WRITE,
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
		ptr[3] = mmap(NULL, SIZE, PROT_NONE, 
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);

		iov[0].iov_base = ptr[0] + (SIZE -1);
		iov[0].iov_len = 1;
		memset(ptr[0], 1, SIZE);

		iov[1].iov_base = ptr[2];
		iov[1].iov_len = SIZE;
		memset(ptr[2], 2, SIZE);

		fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);
		ret = writev(fd, iov, sizeof(iov) / sizeof(struct iovec));
		return 0;
	}	
	/* TESTCASE END*/
	We will get folowing result:
		writev(3, [{"\1", 1}, {"\2"..., 8192}], 2) = -1 EFAULT (Bad address)
 	
	this is hidden bug, and it was invisiable because XXXX_fault_in_readable

return value was ignored before. Lets iov_iter_fault_in_readable perform checks for all segments.

include/linux/fs.h +1 -1
index fef19fc..7e025ea 100644
@@ -433,7 +433,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
 size_t iov_iter_copy_from_user(struct page *page,
 		struct iov_iter *i, unsigned long offset, size_t bytes);
 void iov_iter_advance(struct iov_iter *i, size_t bytes);
-int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t *bytes);
 size_t iov_iter_single_seg_count(struct iov_iter *i);
 
 static inline void iov_iter_init(struct iov_iter *i,
mm/filemap.c +27 -5
index 8d59ed9..8600c3e 100644
@@ -1817,10 +1817,32 @@ void iov_iter_advance(struct iov_iter *i, size_t bytes)
 }
 EXPORT_SYMBOL(iov_iter_advance);
 
-int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t* bytes)
 {
-	char __user *buf = i->iov->iov_base + i->iov_offset;
-	return fault_in_pages_readable(buf, bytes);
+	size_t len = *bytes;
+	int ret;
+	if (likely(i->nr_segs == 1)) {
+		ret = fault_in_pages_readable(i->iov->iov_base, len);
+		if (ret)
+			*bytes = 0;
+	} else {
+		const struct iovec *iov = i->iov;
+		size_t base = i->iov_offset;
+		*bytes = 0;
+		while (len) {
+			int copy = min(len, iov->iov_len - base);
+			if ((ret = fault_in_pages_readable(iov->iov_base + base, copy)))
+				break;
+			*bytes += copy;
+			len -= copy;
+			base += copy;
+			if (iov->iov_len == base) {
+				iov++;
+				base = 0;
+			}
+		}
+	}
+	return ret;	
 }
 EXPORT_SYMBOL(iov_iter_fault_in_readable);
 
@@ -2110,7 +2132,7 @@ static ssize_t generic_perform_write_2copy(struct file *file,
 		 * to check that the address is actually valid, when atomic
 		 * usercopies are used, below.
 		 */
-		if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
+		if (unlikely(iov_iter_fault_in_readable(i, &bytes) && !bytes)) {
 			status = -EFAULT;
 			break;
 		}
@@ -2284,7 +2306,7 @@ again:
 		 * to check that the address is actually valid, when atomic
 		 * usercopies are used, below.
 		 */
-		if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
+		if (unlikely(iov_iter_fault_in_readable(i, &bytes)&& !bytes)) {
 			status = -EFAULT;
 			break;
 		}
#10 lore raw

Re: iov_iter_fault_in_readable fix

Review

Quoted 2 lines · Dmitriy Monakhov
(quoted diff) +0 -0
 	Function prerform check for signgle region, with out respect to
 	segment nature of iovec, For example writev no longer works :)

Btw, could someone please start to collect all sniplets like this in a nice simple regression test suite? If no one wants to start a new one we should probably just put it into xfsqa (which should be useable for other filesystems aswell despite the name)

Quoted 119 lines
	/* TESTCASE BEGIN */
	#include <stdio.h>
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <fcntl.h>
	#include <sys/uio.h>
	#include <sys/mman.h>
	#define SIZE  (4096 * 2)
	int main(int argc, char* argv[])
	{	
		char* ptr[4];
		struct iovec iov[2];
		int fd, ret;
		ptr[0] = mmap(NULL, SIZE, PROT_READ|PROT_WRITE,
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
		ptr[1] = mmap(NULL, SIZE, PROT_NONE,
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
		ptr[2] = mmap(NULL, SIZE, PROT_READ|PROT_WRITE,
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
		ptr[3] = mmap(NULL, SIZE, PROT_NONE, 
			MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);

		iov[0].iov_base = ptr[0] + (SIZE -1);
		iov[0].iov_len = 1;
		memset(ptr[0], 1, SIZE);

		iov[1].iov_base = ptr[2];
		iov[1].iov_len = SIZE;
		memset(ptr[2], 2, SIZE);

		fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);
		ret = writev(fd, iov, sizeof(iov) / sizeof(struct iovec));
		return 0;
	}	
	/* TESTCASE END*/
	We will get folowing result:
		writev(3, [{"\1", 1}, {"\2"..., 8192}], 2) = -1 EFAULT (Bad address)
 	
	this is hidden bug, and it was invisiable because XXXX_fault_in_readable
(quoted diff) +0 -0
 	return value was ignored before. Lets iov_iter_fault_in_readable
 	perform checks for all segments.
include/linux/fs.h +1 -1
index fef19fc..7e025ea 100644
@@ -433,7 +433,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
 size_t iov_iter_copy_from_user(struct page *page,
 		struct iov_iter *i, unsigned long offset, size_t bytes);
 void iov_iter_advance(struct iov_iter *i, size_t bytes);
-int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t *bytes);
 size_t iov_iter_single_seg_count(struct iov_iter *i);
 
 static inline void iov_iter_init(struct iov_iter *i,
mm/filemap.c +27 -6
index 8d59ed9..8600c3e 100644
@@ -1817,10 +1817,32 @@ void iov_iter_advance(struct iov_iter *i, size_t bytes)
 }
 EXPORT_SYMBOL(iov_iter_advance);
 
-int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t* bytes)
 {
-	char __user *buf = i->iov->iov_base + i->iov_offset;
-	return fault_in_pages_readable(buf, bytes);
+	size_t len = *bytes;
+	int ret;
+	if (likely(i->nr_segs == 1)) {
+		ret = fault_in_pages_readable(i->iov->iov_base, len);
+		if (ret)
+			*bytes = 0;
+	} else {
+		const struct iovec *iov = i->iov;
+		size_t base = i->iov_offset;
+		*bytes = 0;
+		while (len) {
+			int copy = min(len, iov->iov_len - base);
+			if ((ret = fault_in_pages_readable(iov->iov_base + base, copy)))
+				break;
+			*bytes += copy;
+			len -= copy;
+			base += copy;
+			if (iov->iov_len == base) {
+				iov++;
+				base = 0;
+			}
+		}
+	}
+	return ret;	
 }
 EXPORT_SYMBOL(iov_iter_fault_in_readable);
 
@@ -2110,7 +2132,7 @@ static ssize_t generic_perform_write_2copy(struct file *file,
 		 * to check that the address is actually valid, when atomic
 		 * usercopies are used, below.
 		 */
-		if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
+		if (unlikely(iov_iter_fault_in_readable(i, &bytes) && !bytes)) {
 			status = -EFAULT;
 			break;
 		}
@@ -2284,7 +2306,7 @@ again:
 		 * to check that the address is actually valid, when atomic
 		 * usercopies are used, below.
 		 */
-		if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
+		if (unlikely(iov_iter_fault_in_readable(i, &bytes)&& !bytes)) {
 			status = -EFAULT;
 			break;
 		}
 
 
-
Quoted 4 lines

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

---end quoted text---

#11 lore raw

Re: iov_iter_fault_in_readable fix

Quoted 8 lines · Christoph Hellwig

Review

Quoted 2 lines · Dmitriy Monakhov
(quoted diff) +0 -0
 	Function prerform check for signgle region, with out respect to
 	segment nature of iovec, For example writev no longer works :)

Btw, could someone please start to collect all sniplets like this in a nice simple regression test suite? If no one wants to start a new one we should probably just put it into xfsqa (which should be useable for other filesystems aswell despite the name)

Yeah, it can run a subset of the tests on NFS and UDF filesystems as well and there are some specific UDF-only tests in it too. I think the NFS test group is mostly generic tests that don't use or test specific XFS features.

I'd be happy to accumulate tests like these in xfsqa...

Cheers,

Dave.

Signature · 3 lines
Dave Chinner
Principal Engineer
SGI Australian Software Group
#12 lore raw

Re: iov_iter_fault_in_readable fix

Quoted 3 lines · David Chinner

Yeah, it can run a subset of the tests on NFS and UDF filesystems as well and there are some specific UDF-only tests in it too. I think the NFS test group is mostly generic tests that don't use or test specific XFS features.

Actually most testcases can run on any reasonable posixish filesystem, we just need some glue to tell the testsuite it's actually okay.

#13 lore raw

Re: iov_iter_fault_in_readable fix

In reply to Christoph Hellwig
Quoted 8 lines · Christoph Hellwig

Review

Quoted 2 lines · Dmitriy Monakhov
(quoted diff) +0 -0
 	Function prerform check for signgle region, with out respect to
 	segment nature of iovec, For example writev no longer works :)

Btw, could someone please start to collect all sniplets like this in a nice simple regression test suite? If no one wants to start a new one we should probably just put it into xfsqa (which should be useable for other filesystems aswell despite the name)

I've prepared testcase (testcases/kernel/syscalls/writev/writev06.c) and sent it to ltp mailing list.

#14 lore raw
In reply to Dmitriy Monakhov
Quoted 20 lines · Dmitriy Monakhov
Quoted 19 lines · On 14:19 ?????? 29 ?????? , [email protected] wrote:

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

Quoted 7 lines
Quoted 4 lines
[[email protected]: API design contributions, code review and fixes]

I've finaly find time to review Nick's "write_begin/write_end aop" patch set. And i have some fixes and questions. My be i've missed somthing and it was already disscussed, but i cant find in LKML.

Thanks, that's really helpful.

Quoted 5 lines

1) loop dev:
loop.c code itself is not perfect. In fact before Nick's patch partial write was't possible. Assumption what write chunks are always page aligned is realy weird ( see "index++" line). Fixed by "new aop loop fix" patch

I think you're right, fix looks good.

Quoted 12 lines

2)block_write_begin:

	After we enter to block_write_begin with *pagep == NULL and
	some page was grabed we remember this page in *pagep
	And if __block_prepare_write() we have to clear *pagep , as 
	it was before. Because this may confuse caller.
	for example caller may have folowing code:
		ret = block_write_begin(..., pagep,...)
		if (ret && *pagep != NULL) {
			unlock_page(*pagep);
			page_cache_release(*pagep);
		}
	Fixed my "new aop block_write_begin fix" patch

I don't think the caller can rely on that if it returns failure. But that is more defensive I guess. Maybe setting it to 1 or so would catch abusers.

Quoted 13 lines

3) __page_symlink:
Nick's patch add folowing code:
+ err = pagecache_write_begin(NULL, mapping, 0,PAGE_CACHE_SIZE,
+ AOP_FLAG_UNINTERRUPTIBLE, &page,&fsdata);
symlink now consume whole page. I have only one question "WHY???". I don't see any advantages, but where are huge list of dissdvantages:
a) fs with blksize == 1k and pagesize == 16k after this patch waste more than 10x times disk space for nothing.
b) What happends if we want use fs with blksize == 4k on i386 after it was used by ia64 ??? (before this patch it was possible).

Quoted 2 lines

I dont prepare patch for this because i dont understand issue witch Nick aimed to fix.

I don't know why myself :P I think it would be just fine to use len-1 as it did previously, so it must have been a typo?

Quoted 8 lines

4) iov_iter_fault_in_readable:
Function prerform check for signgle region, with out respect to segment nature of iovec, For example writev no longer works :) :
writev(3, [{"\1", 1}, {"\2"..., 4096}], 2) = -1 EFAULT (Bad address) this hidden bug, and it was invisiable because XXXX_fault_in_readable return value was ignored before. Lets iov_iter_fault_in_readable perform checks for all segments.
Fixed by :"iov_iter_fault_in_readable fix"

OK thanks. I would rather just change this to use the length of the first iovec always (prefaulting multiple iovecs would have to be benchmarked on real apps that make heavy use of writev, I suspect).

Quoted 10 lines

5) ext3_write_end:

	Before  write_begin/write_end patch set we have folowing locking
	order:
		stop_journal(handle);
		unlock_page(page);
	But now order is oposite:
		unlock_page(page);
		stop_journal(handle);
	Can we got any race condition now? I'm not sure is it actual problem,
	may be somebody cant describe this.

Can we just change it to the original order? That would seem to be safest unless one of the ext3 devs explicitly acks it.

#15 lore raw
Quoted 54 lines · Nick Piggin
Quoted 20 lines · Dmitriy Monakhov
Quoted 19 lines · On 14:19 ?????? 29 ?????? , [email protected] wrote:

The patch titled
fs: introduce write_begin, write_end, and perform_write aops
has been added to the -mm tree. Its filename is
fs-introduce-write_begin-write_end-and-perform_write-aops.patch

*** Remember to use Documentation/SubmitChecklist when testing your code ***

See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this

------------------------------------------------------
Subject: fs: introduce write_begin, write_end, and perform_write aops
From: Nick Piggin <[email protected]>

These are intended to replace prepare_writ eand commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

Quoted 7 lines
Quoted 4 lines
[[email protected]: API design contributions, code review and fixes]

I've finaly find time to review Nick's "write_begin/write_end aop" patch set. And i have some fixes and questions. My be i've missed somthing and it was already disscussed, but i cant find in LKML.

Thanks, that's really helpful.

Quoted 5 lines

1) loop dev:
loop.c code itself is not perfect. In fact before Nick's patch partial write was't possible. Assumption what write chunks are always page aligned is realy weird ( see "index++" line). Fixed by "new aop loop fix" patch

I think you're right, fix looks good.

Quoted 12 lines

2)block_write_begin:

	After we enter to block_write_begin with *pagep == NULL and
	some page was grabed we remember this page in *pagep
	And if __block_prepare_write() we have to clear *pagep , as 
	it was before. Because this may confuse caller.
	for example caller may have folowing code:
		ret = block_write_begin(..., pagep,...)
		if (ret && *pagep != NULL) {
			unlock_page(*pagep);
			page_cache_release(*pagep);
		}
	Fixed my "new aop block_write_begin fix" patch
Quoted 17 lines

I don't think the caller can rely on that if it returns failure. But that is more defensive I guess. Maybe setting it to 1 or so would catch abusers.

Quoted 12 lines

3) __page_symlink:
Nick's patch add folowing code:
+ err = pagecache_write_begin(NULL, mapping, 0,PAGE_CACHE_SIZE,
+ AOP_FLAG_UNINTERRUPTIBLE, &page,&fsdata);
symlink now consume whole page. I have only one question "WHY???". I don't see any advantages, but where are huge list of dissdvantages:
a) fs with blksize == 1k and pagesize == 16k after this patch waste more than 10x times disk space for nothing.
b) What happends if we want use fs with blksize == 4k on i386 after it was used by ia64 ??? (before this patch it was possible).

One more visiable effect caused by wrong symlink size:
fsstress cause folowing error:
<LOG BEGIN>
EXT3-fs unexpected failure: !buffer_revoked(bh); inconsistent data on disk
ext3_forget: aborting transaction: IO failure in __ext3_journal_revoke ext3_abort called.

EXT3-fs error (device dm-4): ext3_forget: error -5 when attempting revoke
Remounting filesystem read-only
Aborting journal on device dm-4.
journal commit I/O error
journal commit I/O error
journal commit I/O error
journal commit I/O error
journal commit I/O error
journal commit I/O error
EXT3-fs error (device dm-4) in ext3_free_blocks_sb: Journal has aborted

journal commit I/O error
journal commit I/O error
EXT3-fs error (device dm-4) in ext3_reserve_inode_write: Journal has aborted
EXT3-fs error (device dm-4) in ext3_truncate: IO failure journal commit I/O error
journal commit I/O error
journal commit I/O error
journal commit I/O error
EXT3-fs error (device dm-4) in ext3_reserve_inode_write: Journal has aborted
EXT3-fs error (device dm-4) in ext3_orphan_del: Journal has aborted EXT3-fs error (device dm-4) in ext3_reserve_inode_write: Journal has aborted
EXT3-fs error (device dm-4) in ext3_delete_inode: IO failure <LOG END>

After symlink size was fixed to len-1 problem dissappeared.

Quoted 41 lines
Quoted 3 lines

I dont prepare patch for this because i dont understand issue witch Nick aimed to fix.

I don't know why myself :P I think it would be just fine to use len-1 as it did previously, so it must have been a typo?

Quoted 8 lines

4) iov_iter_fault_in_readable:
Function prerform check for signgle region, with out respect to segment nature of iovec, For example writev no longer works :) :
writev(3, [{"\1", 1}, {"\2"..., 4096}], 2) = -1 EFAULT (Bad address) this hidden bug, and it was invisiable because XXXX_fault_in_readable return value was ignored before. Lets iov_iter_fault_in_readable perform checks for all segments.
Fixed by :"iov_iter_fault_in_readable fix"

OK thanks. I would rather just change this to use the length of the first iovec always (prefaulting multiple iovecs would have to be benchmarked on real apps that make heavy use of writev, I suspect).

Quoted 10 lines

5) ext3_write_end:

	Before  write_begin/write_end patch set we have folowing locking
	order:
		stop_journal(handle);
		unlock_page(page);
	But now order is oposite:
		unlock_page(page);
		stop_journal(handle);
	Can we got any race condition now? I'm not sure is it actual problem,
	may be somebody cant describe this.

Can we just change it to the original order? That would seem to be safest unless one of the ext3 devs explicitly acks it.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#16 lore raw
In reply to Nick Piggin

On Wed, 2007-06-13 at 13:43 +0200, Nick Piggin wrote:
..

Quoted 12 lines
Quoted 11 lines

5) ext3_write_end:

	Before  write_begin/write_end patch set we have folowing locking
	order:
		stop_journal(handle);
		unlock_page(page);
	But now order is oposite:
		unlock_page(page);
		stop_journal(handle);
	Can we got any race condition now? I'm not sure is it actual problem,
	may be somebody cant describe this.
Quoted 2 lines

Can we just change it to the original order? That would seem to be safest unless one of the ext3 devs explicitly acks it.

It would be nice to go back to original order, but its not that simple with current structure of the code. With Nick's patches unlock_page() happens in generic_write_end(). journal_stop() needs to happen after generic_write_end(). :(

Mingming, can you take a look at the current & proposed order ? I ran into bunch of races when I tried to change the order for ->writepages() support earlier :(

Thanks,
Badari

#17 lore raw
Quoted 14 lines · Badari Pulavarty

On Wed, 2007-06-13 at 13:43 +0200, Nick Piggin wrote:
..

Quoted 12 lines
Quoted 11 lines

5) ext3_write_end:

	Before  write_begin/write_end patch set we have folowing locking
	order:
		stop_journal(handle);
		unlock_page(page);
	But now order is oposite:
		unlock_page(page);
		stop_journal(handle);
	Can we got any race condition now? I'm not sure is it actual problem,
	may be somebody cant describe this.
Quoted 7 lines
Quoted 2 lines

Can we just change it to the original order? That would seem to be safest unless one of the ext3 devs explicitly acks it.

It would be nice to go back to original order, but its not that simple with current structure of the code. With Nick's patches unlock_page() happens in generic_write_end(). journal_stop() needs to happen after generic_write_end(). :(

Well we could use block_write_end?

Quoted 3 lines

Mingming, can you take a look at the current & proposed order ? I ran into bunch of races when I tried to change the order for ->writepages() support earlier :(

OK, it sounds like we probably want to revert to the original order at least for this patchset. If the new order is proven safe then that could be introduced later to simplify things...

Thanks,
Nick

maintainer
#18 lore raw
In reply to Badari Pulavarty
Quoted 14 lines · Nick Piggin

..

Quoted 12 lines
Quoted 11 lines

5) ext3_write_end:

	Before  write_begin/write_end patch set we have folowing locking
	order:
		stop_journal(handle);
		unlock_page(page);
	But now order is oposite:
		unlock_page(page);
		stop_journal(handle);
	Can we got any race condition now? I'm not sure is it actual problem,
	may be somebody cant describe this.
Quoted 2 lines
Quoted 2 lines

Can we just change it to the original order? That would seem to be safest unless one of the ext3 devs explicitly acks it.

Sorry, I've missed beginning of this thread. But what problems can
exactly cause this ordering change? ext3_journal_stop has no need to be protected by the page lock - it can be even better that it's not protected as it can trigger commit and all that would happen unnecessarily under page lock...

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SuSE CR Labs
#19 lore raw
Quoted 14 lines · Jan Kara
Quoted 14 lines · Nick Piggin

..

Quoted 12 lines
Quoted 11 lines

5) ext3_write_end:

	Before  write_begin/write_end patch set we have folowing locking
	order:
		stop_journal(handle);
		unlock_page(page);
	But now order is oposite:
		unlock_page(page);
		stop_journal(handle);
	Can we got any race condition now? I'm not sure is it actual problem,
	may be somebody cant describe this.
Quoted 7 lines
Quoted 2 lines
Quoted 2 lines

Can we just change it to the original order? That would seem to be safest unless one of the ext3 devs explicitly acks it.

Sorry, I've missed beginning of this thread. But what problems can
exactly cause this ordering change? ext3_journal_stop has no need to be protected by the page lock - it can be even better that it's not protected as it can trigger commit and all that would happen unnecessarily under page lock...

Sure, if you think it is safe. I would rather it be done in a different patch though.