@@ -946,6 +946,12 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
goto out;
}
+ /*
+ * We're falling back to buffered writes, increase the
+ * dio_write_fallback counter.
+ */
+ atomic_inc(&fs_info->io_stats.dio_write_fallback);
+
pos = iocb->ki_pos;
written_buffered = btrfs_buffered_write(iocb, from);
if (written_buffered < 0) {
@@ -3644,10 +3644,19 @@ static ssize_t btrfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
ssize_t ret = 0;
if (iocb->ki_flags & IOCB_DIRECT) {
+ struct btrfs_fs_info *fs_info;
+
ret = btrfs_direct_read(iocb, to);
if (ret < 0 || !iov_iter_count(to) ||
iocb->ki_pos >= i_size_read(file_inode(iocb->ki_filp)))
return ret;
+
+ /*
+ * We're falling back to buffered reads, increase the
+ * dio_read_fallback counter.
+ */
+ fs_info = BTRFS_I(iocb->ki_filp->f_inode)->root->fs_info;
+ atomic_inc(&fs_info->io_stats.dio_read_fallback);
}
return filemap_read(iocb, to, ret);
@@ -406,6 +406,12 @@ struct btrfs_commit_stats {
u64 total_commit_dur;
};
+/* Store data about I/O stats, exported via sysfs. */
+struct btrfs_io_stats {
+ atomic_t dio_read_fallback;
+ atomic_t dio_write_fallback;
+};
+
struct btrfs_fs_info {
u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
unsigned long flags;
@@ -851,6 +857,8 @@ struct btrfs_fs_info {
/* Updates are not protected by any lock */
struct btrfs_commit_stats commit_stats;
+ struct btrfs_io_stats io_stats;
+
/*
* Last generation where we dropped a non-relocation root.
* Use btrfs_set_last_root_drop_gen() and btrfs_get_last_root_drop_gen()
@@ -1526,6 +1526,20 @@ static ssize_t btrfs_bg_reclaim_threshold_store(struct kobject *kobj,
BTRFS_ATTR_RW(, bg_reclaim_threshold, btrfs_bg_reclaim_threshold_show,
btrfs_bg_reclaim_threshold_store);
+static ssize_t btrfs_io_stats_show(struct kobject *kobj,
+ struct kobj_attribute *a, char *buf)
+{
+ struct btrfs_fs_info *fs_info = to_fs_info(kobj);
+
+ return sysfs_emit(buf,
+ "dio_read_fallback %u\n"
+ "dio_write_fallback %u\n",
+ atomic_read(&fs_info->io_stats.dio_read_fallback),
+ atomic_read(&fs_info->io_stats.dio_write_fallback));
+}
+
+BTRFS_ATTR(, io_stats, btrfs_io_stats_show);
+
#ifdef CONFIG_BTRFS_EXPERIMENTAL
static ssize_t btrfs_offload_csum_show(struct kobject *kobj,
struct kobj_attribute *a, char *buf)
@@ -1586,6 +1600,7 @@ static const struct attribute *btrfs_attrs[] = {
BTRFS_ATTR_PTR(, bg_reclaim_threshold),
BTRFS_ATTR_PTR(, commit_stats),
BTRFS_ATTR_PTR(, temp_fsid),
+ BTRFS_ATTR_PTR(, io_stats),
#ifdef CONFIG_BTRFS_EXPERIMENTAL
BTRFS_ATTR_PTR(, offload_csum),
#endif
For O_DIRECT reads and writes, both the buffer address and the file offset need to be aligned to the block size. Otherwise, btrfs falls back to doing buffered I/O, which is probably not what you want. It also creates portability issues, as not all filesystems do this. Add a new sysfs entry io_stats, to record how many times DIO falls back to doing buffered I/O. The intention is that once this is recorded, we can investigate the programs running on any machine where this isn't 0. Signed-off-by: Mark Harmstone <maharmstone@fb.com> --- fs/btrfs/direct-io.c | 6 ++++++ fs/btrfs/file.c | 9 +++++++++ fs/btrfs/fs.h | 8 ++++++++ fs/btrfs/sysfs.c | 15 +++++++++++++++ 4 files changed, 38 insertions(+)