aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishal Verma <vishal.l.verma@intel.com>2017-07-25 15:00:20 -0600
committerVishal Verma <vishal.l.verma@intel.com>2017-07-25 15:00:20 -0600
commitcece7623fce4b895c75b89c4b62851710d9e1817 (patch)
treeacc4079088b82bf58b9930cc6846deddbc2888e3
parentffa2cbd1c3c105a41e7e00df4d3990214a941b7e (diff)
downloadnvdimm-dax-err.tar.gz
dax: fallback to non-dax IO in the presence of badblocksdax-err
If a file system is mounted with dax, all IO, including read(2) and write(2) go through dax. Since dax is unable to clear media errors (badblocks), this leaves us with no way to clear these errors for a dax filesystem. Fix this by adding a check for badblocks at the bdev level, and when checking if we want to do dax IO from filesystems, only make that optimization if there are no badblocks. In the presence of badblocks, fall back to the regular (O_DIRECT or page cache) IO that goes through the block driver, which is able to handle badblock clearing as needed. Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Cc: Dan Williams <dan.j.williams@intel.com> Cc: Jeff Moyer <jmoyer@redhat.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Dave Chinner <david@fromorbit.com> Cc: Darrick J. Wong <darrick.wong@oracle.com> Cc: Jan Kara <jack@suse.com> Cc: Martin K. Petersen <martin.petersen@oracle.com> Cc: Jens Axboe <axboe@kernel.dk> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
-rw-r--r--block/badblocks.c15
-rw-r--r--block/genhd.c6
-rw-r--r--fs/ext4/file.c2
-rw-r--r--fs/xfs/xfs_file.c2
-rw-r--r--include/linux/badblocks.h1
-rw-r--r--include/linux/fs.h15
6 files changed, 39 insertions, 2 deletions
diff --git a/block/badblocks.c b/block/badblocks.c
index 43c71166e1e2a..aefacf19a18a8 100644
--- a/block/badblocks.c
+++ b/block/badblocks.c
@@ -464,6 +464,21 @@ void ack_all_badblocks(struct badblocks *bb)
EXPORT_SYMBOL_GPL(ack_all_badblocks);
/**
+ * badblocks_present() - check whether any badblocks are present
+ * @bb: the badblocks structure that holds all badblock information
+ *
+ * Return:
+ * 'true' if badblocks are present, 'false' otherwise
+ */
+bool badblocks_present(struct badblocks *bb)
+{
+ if (bb && bb->count)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL_GPL(badblocks_present);
+
+/**
* badblocks_show() - sysfs access to bad-blocks list
* @bb: the badblocks structure that holds all badblock information
* @page: buffer received from sysfs
diff --git a/block/genhd.c b/block/genhd.c
index 7f520fa25d16d..bdf572666aa39 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1441,6 +1441,12 @@ int invalidate_partition(struct gendisk *disk, int partno)
EXPORT_SYMBOL(invalidate_partition);
+bool bdev_has_badblocks(struct block_device *bdev)
+{
+ return badblocks_present(bdev->bd_disk->bb);
+}
+EXPORT_SYMBOL(bdev_has_badblocks);
+
/*
* Disk events - monitor disk events like media change and eject request.
*/
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 58294c9a7e1df..ac384a2450071 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -220,7 +220,7 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
return -EIO;
#ifdef CONFIG_FS_DAX
- if (IS_DAX(inode))
+ if (IS_DAX(inode) && !has_badblocks(inode))
return ext4_dax_write_iter(iocb, from);
#endif
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index c4893e226fd8e..01f2e6ee54e59 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -710,7 +710,7 @@ xfs_file_write_iter(
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return -EIO;
- if (IS_DAX(inode))
+ if (IS_DAX(inode) && !has_badblocks(inode))
ret = xfs_file_dax_write(iocb, from);
else if (iocb->ki_flags & IOCB_DIRECT) {
/*
diff --git a/include/linux/badblocks.h b/include/linux/badblocks.h
index c3bdf8c594800..801829d151b1b 100644
--- a/include/linux/badblocks.h
+++ b/include/linux/badblocks.h
@@ -49,6 +49,7 @@ void ack_all_badblocks(struct badblocks *bb);
ssize_t badblocks_show(struct badblocks *bb, char *page, int unack);
ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
int unack);
+bool badblocks_present(struct badblocks *bb);
int badblocks_init(struct badblocks *bb, int enable);
void badblocks_exit(struct badblocks *bb);
struct device;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7b5d6816542b7..0918e25bd9ed7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2859,6 +2859,7 @@ extern void inode_sb_list_add(struct inode *inode);
#ifdef CONFIG_BLOCK
extern int bdev_read_only(struct block_device *);
#endif
+extern bool bdev_has_badblocks(struct block_device *);
extern int set_blocksize(struct block_device *, int);
extern int sb_set_blocksize(struct super_block *, int);
extern int sb_min_blocksize(struct super_block *, int);
@@ -3258,6 +3259,20 @@ int __init get_filesystem_list(char *buf);
#define OPEN_FMODE(flag) ((__force fmode_t)(((flag + 1) & O_ACCMODE) | \
(flag & __FMODE_NONOTIFY)))
+#ifdef CONFIG_BLOCK
+static inline bool has_badblocks(struct inode *inode)
+{
+ if (S_ISBLK(inode->i_mode) && inode->i_bdev)
+ return bdev_has_badblocks(inode->i_bdev);
+ return false;
+}
+#else
+static inline bool has_badblocks(struct inode *inode)
+{
+ return false;
+}
+#endif
+
static inline bool is_sxid(umode_t mode)
{
return (mode & S_ISUID) || ((mode & S_ISGID) && (mode & S_IXGRP));