summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2013-10-25 19:05:54 +0100
committerJens Axboe <axboe@kernel.dk>2013-10-25 19:05:54 +0100
commit945514c44435b3a7625d0ce3cbd002b3d246b70b (patch)
tree1b19e246231e6ee4a659393ef66800e1bea96a3e
parent280d45f6c35d8d7a0fe20c36caf426e3ac139cf9 (diff)
direct-io: only inc/dec inode->i_dio_count for file systemsblk-mq/misc
We don't need truncate protection for block devices, so add a flag bypassing this cache line dirtying twice for every IO. This easily contributes to 5-10% of the CPU time on high IOPS O_DIRECT testing. Cc: Christoph Hellwig <hch@infradead.org> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: akpm@linux-foundation.org Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--fs/block_dev.c3
-rw-r--r--fs/btrfs/inode.c4
-rw-r--r--fs/direct-io.c8
-rw-r--r--fs/ext4/indirect.c4
-rw-r--r--fs/ext4/inode.c2
-rw-r--r--fs/inode.c18
-rw-r--r--fs/nfs/direct.c4
-rw-r--r--include/linux/fs.h6
8 files changed, 33 insertions, 16 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1e86823a9cbd..e20b7c1dcbae 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -172,7 +172,8 @@ blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
struct inode *inode = file->f_mapping->host;
return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iov, offset,
- nr_segs, blkdev_get_block, NULL, NULL, 0);
+ nr_segs, blkdev_get_block, NULL, NULL,
+ DIO_IGNORE_TRUNCATE);
}
int __sync_blockdev(struct block_device *bdev, int wait)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index b0ef7b07b1b3..2d51e1248364 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7238,7 +7238,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
goto out;
} else if (unlikely(test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
&BTRFS_I(inode)->runtime_flags))) {
- inode_dio_done(inode);
+ inode_dio_done(inode, 0);
flags = DIO_LOCKING | DIO_SKIP_HOLES;
wakeup = false;
}
@@ -7258,7 +7258,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
}
out:
if (wakeup)
- inode_dio_done(inode);
+ inode_dio_done(inode, 0);
if (relock)
mutex_lock(&inode->i_mutex);
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 0e04142d5962..5aa297b69003 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -265,7 +265,8 @@ static ssize_t dio_complete(struct dio *dio, loff_t offset, ssize_t ret,
if (dio->end_io && dio->result)
dio->end_io(dio->iocb, offset, transferred, dio->private);
- inode_dio_done(dio->inode);
+ inode_dio_done(dio->inode, dio->flags);
+
if (is_async) {
if (dio->rw & WRITE) {
int err;
@@ -1222,10 +1223,7 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
}
}
- /*
- * Will be decremented at I/O completion time.
- */
- atomic_inc(&inode->i_dio_count);
+ inode_dio_start(inode, dio->flags);
retval = 0;
sdio.blkbits = blkbits;
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 594009f5f523..5dc9fadd36ea 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -683,14 +683,14 @@ retry:
smp_mb();
if (unlikely(ext4_test_inode_state(inode,
EXT4_STATE_DIOREAD_LOCK))) {
- inode_dio_done(inode);
+ inode_dio_done(inode, 0);
goto locked;
}
ret = __blockdev_direct_IO(rw, iocb, inode,
inode->i_sb->s_bdev, iov,
offset, nr_segs,
ext4_get_block, NULL, NULL, 0);
- inode_dio_done(inode);
+ inode_dio_done(inode, 0);
} else {
locked:
ret = blockdev_direct_IO(rw, iocb, inode, iov,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index e274e9c1171f..87b8c29b9f27 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3193,7 +3193,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
retake_lock:
if (rw == WRITE)
- inode_dio_done(inode);
+ inode_dio_done(inode, 0);
/* take i_mutex locking again if we do a ovewrite dio */
if (overwrite) {
up_read(&EXT4_I(inode)->i_data_sem);
diff --git a/fs/inode.c b/fs/inode.c
index b33ba8e021cc..2a1819cd8a02 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1854,6 +1854,20 @@ void inode_dio_wait(struct inode *inode)
}
EXPORT_SYMBOL(inode_dio_wait);
+void inode_dio_start(struct inode *inode, int dio_flags)
+{
+ if (!(dio_flags & DIO_IGNORE_TRUNCATE))
+ atomic_inc(&inode->i_dio_count);
+}
+
+static int inode_dio_dec(struct inode *inode, int dio_flags)
+{
+ if (!(dio_flags & DIO_IGNORE_TRUNCATE))
+ return atomic_dec_and_test(&inode->i_dio_count);
+
+ return 0;
+}
+
/*
* inode_dio_done - signal finish of a direct I/O requests
* @inode: inode the direct I/O happens on
@@ -1861,9 +1875,9 @@ EXPORT_SYMBOL(inode_dio_wait);
* This is called once we've finished processing a direct I/O request,
* and is used to wake up callers waiting for direct I/O to be quiesced.
*/
-void inode_dio_done(struct inode *inode)
+void inode_dio_done(struct inode *inode, int dio_flags)
{
- if (atomic_dec_and_test(&inode->i_dio_count))
+ if (inode_dio_dec(inode, dio_flags))
wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
}
EXPORT_SYMBOL(inode_dio_done);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 91ff089d3412..8bfa04b4504c 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -476,7 +476,7 @@ out:
static void nfs_inode_dio_write_done(struct inode *inode)
{
nfs_zap_mapping(inode, inode->i_mapping);
- inode_dio_done(inode);
+ inode_dio_done(inode, 0);
}
#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
@@ -833,7 +833,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
* generic layer handle the completion.
*/
if (requested_bytes == 0) {
- inode_dio_done(inode);
+ inode_dio_done(inode, 0);
nfs_direct_req_release(dreq);
return result < 0 ? result : -EIO;
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3f40547ba191..a1c23f8a874d 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2468,6 +2468,9 @@ enum {
/* filesystem does not support filling holes */
DIO_SKIP_HOLES = 0x02,
+
+ /* inode/fs/bdev does not need truncate protection */
+ DIO_IGNORE_TRUNCATE = 0x04,
};
void dio_end_io(struct bio *bio, int error);
@@ -2488,7 +2491,8 @@ static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
#endif
void inode_dio_wait(struct inode *inode);
-void inode_dio_done(struct inode *inode);
+void inode_dio_done(struct inode *inode, int dio_flags);
+void inode_dio_start(struct inode *inode, int dio_flags);
extern const struct file_operations generic_ro_fops;