linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ian Abbott <abbotti@mev.co.uk>
To: lkml <linux-kernel@vger.kernel.org>
Cc: Jan Kara <jack@suze.cz>, Ian Abbott <abbotti@mev.co.uk>
Subject: [PATCH v3] UDF: Add support for O_DIRECT
Date: Tue,  4 Sep 2012 10:49:39 +0100	[thread overview]
Message-ID: <1346752179-28052-1-git-send-email-abbotti@mev.co.uk> (raw)
In-Reply-To: <1343731212-4381-1-git-send-email-abbotti@mev.co.uk>

Add support for the O_DIRECT flag.  There are two cases to deal with:

1. Small files stored in the ICB (inode control block?): just return 0
from the new udf_adinicb_direct_IO() handler to fall back to buffered
I/O.  For direct writes, there is a "gotcha" to deal with when
generic_file_direct_write() in mm/filemap.c invalidates the pages.  In
the udf_adinicb_writepage() handler, only part of the page data will be
valid and the rest will be zeroed out, so only copy the valid part into
the ICB.  (This is actually a bit inefficient as udf_adinicb_write_end()
will have already copied the data into the ICB once, but it's pretty
likely that the file will grow to the point where its data can no longer
be stored in the ICB and will be moved to a different area of the file
system.  At that point, a different direct_IO handler will be used - see
below.)

2. Larger files, not stored in the ICB: nothing special here.  Just call
blockdev_direct_IO() from our new udf_direct_IO() handler and tidy up
any blocks instantiated outside i_size on error.  This is pretty
standard.  Factor error handling code out of udf_write_begin() into new
function udf_write_failed() so it can also be called by udf_direct_IO().

Also change the whitespace in udf_aops and udf_adinicb_aops to make them
a bit neater.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
---
v2: Rework error handling in udf_direct_IO to avoid calling deprecated
    vmtruncate().
v3: Rebased to next-20120904.
---
 fs/udf/file.c  | 29 +++++++++++++++++++++++++----
 fs/udf/inode.c | 52 ++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 61 insertions(+), 20 deletions(-)

diff --git a/fs/udf/file.c b/fs/udf/file.c
index 7f3f7ba..f5f9ddd 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -34,6 +34,7 @@
 #include <linux/errno.h>
 #include <linux/pagemap.h>
 #include <linux/buffer_head.h>
+#include <linux/writeback.h>
 #include <linux/aio.h>
 
 #include "udf_i.h"
@@ -64,12 +65,23 @@ static int udf_adinicb_writepage(struct page *page,
 	struct inode *inode = page->mapping->host;
 	char *kaddr;
 	struct udf_inode_info *iinfo = UDF_I(inode);
+	loff_t start, end, page_start, page_offset;
 
 	BUG_ON(!PageLocked(page));
 
 	kaddr = kmap(page);
-	memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr, kaddr, inode->i_size);
-	mark_inode_dirty(inode);
+	/* The beginning and/or end of the page data is likely to be invalid
+	 * for O_DIRECT, so only copy the valid part. */
+	page_start = (loff_t)page->index << PAGE_CACHE_SHIFT;
+	start = max(page_start, wbc->range_start);
+	end = min3(page_start + (loff_t)PAGE_CACHE_SIZE - 1,
+		   wbc->range_end, inode->i_size - 1);
+	if (likely(start <= end)) {
+		page_offset = start - page_start;
+		memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr + start,
+		       kaddr + page_offset, (end + 1 - start));
+		mark_inode_dirty(inode);
+	}
 	SetPageUptodate(page);
 	kunmap(page);
 	unlock_page(page);
@@ -95,11 +107,20 @@ static int udf_adinicb_write_end(struct file *file,
 	return simple_write_end(file, mapping, pos, len, copied, page, fsdata);
 }
 
+static ssize_t udf_adinicb_direct_IO(int rw, struct kiocb *iocb,
+				     const struct iovec *iov,
+				     loff_t offset, unsigned long nr_segs)
+{
+	/* Fallback to buffered I/O. */
+	return 0;
+}
+
 const struct address_space_operations udf_adinicb_aops = {
 	.readpage	= udf_adinicb_readpage,
 	.writepage	= udf_adinicb_writepage,
-	.write_begin = simple_write_begin,
-	.write_end = udf_adinicb_write_end,
+	.write_begin	= simple_write_begin,
+	.write_end	= udf_adinicb_write_end,
+	.direct_IO	= udf_adinicb_direct_IO,
 };
 
 static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 1a0588e..b905448 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -95,6 +95,22 @@ void udf_evict_inode(struct inode *inode)
 	}
 }
 
+static void udf_write_failed(struct address_space *mapping, loff_t to)
+{
+	struct inode *inode = mapping->host;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	loff_t isize = inode->i_size;
+
+	if (to > isize) {
+		truncate_pagecache(inode, to, isize);
+		if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+			down_write(&iinfo->i_data_sem);
+			udf_truncate_extents(inode);
+			up_write(&iinfo->i_data_sem);
+		}
+	}
+}
+
 static int udf_writepage(struct page *page, struct writeback_control *wbc)
 {
 	return block_write_full_page(page, udf_get_block, wbc);
@@ -124,21 +140,24 @@ static int udf_write_begin(struct file *file, struct address_space *mapping,
 	int ret;
 
 	ret = block_write_begin(mapping, pos, len, flags, pagep, udf_get_block);
-	if (unlikely(ret)) {
-		struct inode *inode = mapping->host;
-		struct udf_inode_info *iinfo = UDF_I(inode);
-		loff_t isize = inode->i_size;
-
-		if (pos + len > isize) {
-			truncate_pagecache(inode, pos + len, isize);
-			if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
-				down_write(&iinfo->i_data_sem);
-				udf_truncate_extents(inode);
-				up_write(&iinfo->i_data_sem);
-			}
-		}
-	}
+	if (unlikely(ret))
+		udf_write_failed(mapping, pos + len);
+	return ret;
+}
 
+static ssize_t udf_direct_IO(int rw, struct kiocb *iocb,
+			     const struct iovec *iov,
+			     loff_t offset, unsigned long nr_segs)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	ssize_t ret;
+
+	ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
+				  udf_get_block);
+	if (unlikely(ret < 0 && (rw && WRITE)))
+		udf_write_failed(mapping, offset + iov_length(iov, nr_segs));
 	return ret;
 }
 
@@ -152,8 +171,9 @@ const struct address_space_operations udf_aops = {
 	.readpages	= udf_readpages,
 	.writepage	= udf_writepage,
 	.writepages	= udf_writepages,
-	.write_begin		= udf_write_begin,
-	.write_end		= generic_write_end,
+	.write_begin	= udf_write_begin,
+	.write_end	= generic_write_end,
+	.direct_IO	= udf_direct_IO,
 	.bmap		= udf_bmap,
 };
 
-- 
1.7.12


  reply	other threads:[~2012-09-04  9:57 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-07-24 12:17 [PATCH] " Ian Abbott
2012-07-31 10:40 ` [PATCH v2] " Ian Abbott
2012-09-04  9:49   ` Ian Abbott [this message]
2012-09-04 14:39     ` [PATCH v3] " Jan Kara
2012-09-04 15:11       ` Ian Abbott
2012-09-05  9:03         ` Ian Abbott
2012-09-05 12:05         ` Jan Kara
2012-09-05 12:55           ` Jan Kara
2012-09-05 16:00             ` Jan Kara
2012-09-05 16:44               ` [PATCH v4] " Ian Abbott
2012-09-05 19:02                 ` Jan Kara
2012-09-06 10:08                   ` [PATCH] UDF: Fix incorrect error handling in udf_direct_IO() Ian Abbott
2012-09-06 14:22                     ` Jan Kara

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1346752179-28052-1-git-send-email-abbotti@mev.co.uk \
    --to=abbotti@mev.co.uk \
    --cc=jack@suze.cz \
    --cc=linux-kernel@vger.kernel.org \
    --subject='Re: [PATCH v3] UDF: Add support for O_DIRECT' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).