From 26a3f3c7d9aef02bc9daef162111c347025d5866 Mon Sep 17 00:00:00 2001 From: Coleman Kane Date: Sun, 18 Oct 2020 12:54:21 -0400 Subject: [PATCH] Linux 5.10 compat: check_disk_change() removed Kernel 5.10 removed check_disk_change() in favor of callers using the faster bdev_check_media_change() instead, and explicitly forcing bdev revalidation when they desire that behavior. To preserve prior behavior, I have wrapped this into a zfs_check_media_change() macro that calls an inline function for the new API that mimics the old behavior when check_disk_change() doesn't exist, and just calls check_disk_change() if it exists. Reviewed-by: Brian Behlendorf Signed-off-by: Coleman Kane Closes #11085 --- config/kernel-blkdev-change.m4 | 62 ++++++++++++++++++++++++++++++++++ config/kernel.m4 | 2 ++ include/linux/blkdev_compat.h | 36 ++++++++++++++++++++ module/zfs/vdev_disk.c | 2 +- module/zfs/zvol.c | 2 +- 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 config/kernel-blkdev-change.m4 diff --git a/config/kernel-blkdev-change.m4 b/config/kernel-blkdev-change.m4 new file mode 100644 index 0000000000..acaf12b892 --- /dev/null +++ b/config/kernel-blkdev-change.m4 @@ -0,0 +1,62 @@ +dnl # +dnl # check_disk_change() was removed in 5.10 +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_BLKDEV_CHECK_DISK_CHANGE], [ + ZFS_LINUX_TEST_SRC([check_disk_change], [ + #include + #include + ], [ + struct block_device *bdev = NULL; + bool error; + + error = check_disk_change(bdev); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_BLKDEV_CHECK_DISK_CHANGE], [ + AC_MSG_CHECKING([whether check_disk_change() exists]) + ZFS_LINUX_TEST_RESULT([check_disk_change], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_CHECK_DISK_CHANGE, 1, + [check_disk_change() exists]) + ], [ + AC_MSG_RESULT(no) + ]) +]) + +dnl # +dnl # 5.10 API, check_disk_change() is removed, in favor of +dnl # bdev_check_media_change(), which doesn't force revalidation +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_BLKDEV_BDEV_CHECK_MEDIA_CHANGE], [ + ZFS_LINUX_TEST_SRC([bdev_check_media_change], [ + #include + #include + ], [ + struct block_device *bdev = NULL; + int error; + + error = bdev_check_media_change(bdev); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_BLKDEV_BDEV_CHECK_MEDIA_CHANGE], [ + AC_MSG_CHECKING([whether bdev_disk_changed() exists]) + ZFS_LINUX_TEST_RESULT([bdev_check_media_change], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_BDEV_CHECK_MEDIA_CHANGE, 1, + [bdev_check_media_change() exists]) + ], [ + AC_MSG_RESULT(no) + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_SRC_BLKDEV_CHANGE], [ + ZFS_AC_KERNEL_SRC_BLKDEV_CHECK_DISK_CHANGE + ZFS_AC_KERNEL_SRC_BLKDEV_BDEV_CHECK_MEDIA_CHANGE +]) + +AC_DEFUN([ZFS_AC_KERNEL_BLKDEV_CHANGE], [ + ZFS_AC_KERNEL_BLKDEV_CHECK_DISK_CHANGE + ZFS_AC_KERNEL_BLKDEV_BDEV_CHECK_MEDIA_CHANGE +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index 744ef2da7b..ae9b3f2fc3 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -144,6 +144,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_TOTALRAM_PAGES_FUNC ZFS_AC_KERNEL_SRC_TOTALHIGH_PAGES ZFS_AC_KERNEL_SRC_KSTRTOUL + ZFS_AC_KERNEL_SRC_BLKDEV_CHANGE AC_MSG_CHECKING([for available kernel interfaces]) ZFS_LINUX_TEST_COMPILE_ALL([kabi]) @@ -263,6 +264,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_TOTALRAM_PAGES_FUNC ZFS_AC_KERNEL_TOTALHIGH_PAGES ZFS_AC_KERNEL_KSTRTOUL + ZFS_AC_KERNEL_BLKDEV_CHANGE ]) dnl # diff --git a/include/linux/blkdev_compat.h b/include/linux/blkdev_compat.h index 101d6becda..9c8b9a2c78 100644 --- a/include/linux/blkdev_compat.h +++ b/include/linux/blkdev_compat.h @@ -371,12 +371,48 @@ bio_set_bi_error(struct bio *bio, int error) * * For older kernels trigger a re-reading of the partition table by calling * check_disk_change() which calls flush_disk() to invalidate the device. + * + * For newer kernels (as of 5.10), bdev_check_media_chage is used, in favor of + * check_disk_change(), with the modification that invalidation is no longer + * forced. */ +#ifdef HAVE_CHECK_DISK_CHANGE +#define zfs_check_media_change(bdev) check_disk_change(bdev) #ifdef HAVE_BLKDEV_REREAD_PART #define vdev_bdev_reread_part(bdev) blkdev_reread_part(bdev) #else #define vdev_bdev_reread_part(bdev) check_disk_change(bdev) #endif /* HAVE_BLKDEV_REREAD_PART */ +#else +#ifdef HAVE_BDEV_CHECK_MEDIA_CHANGE +static inline int +zfs_check_media_change(struct block_device *bdev) +{ + struct gendisk *gd = bdev->bd_disk; + const struct block_device_operations *bdo = gd->fops; + + if (!bdev_check_media_change(bdev)) + return (0); + + /* + * Force revalidation, to mimic the old behavior of + * check_disk_change() + */ + if (bdo->revalidate_disk) + bdo->revalidate_disk(gd); + + return (0); +} +#define vdev_bdev_reread_part(bdev) zfs_check_media_change(bdev) +#else +/* + * This is encountered if check_disk_change() and bdev_check_media_change() + * are not available in the kernel - likely due to an API change that needs + * to be chased down. + */ +#error "Unsupported kernel: no usable disk change check" +#endif /* HAVE_BDEV_CHECK_MEDIA_CHANGE */ +#endif /* HAVE_CHECK_DISK_CHANGE */ /* * 2.6.22 API change diff --git a/module/zfs/vdev_disk.c b/module/zfs/vdev_disk.c index 94e4c9a1a3..17b49277f6 100644 --- a/module/zfs/vdev_disk.c +++ b/module/zfs/vdev_disk.c @@ -848,7 +848,7 @@ vdev_disk_io_done(zio_t *zio) vdev_t *v = zio->io_vd; vdev_disk_t *vd = v->vdev_tsd; - if (check_disk_change(vd->vd_bdev)) { + if (zfs_check_media_change(vd->vd_bdev)) { vdev_bdev_invalidate(vd->vd_bdev); v->vdev_remove_wanted = B_TRUE; spa_async_request(zio->io_spa, SPA_ASYNC_REMOVE); diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index e6e6125449..dbf49e3d9c 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -1450,7 +1450,7 @@ zvol_open(struct block_device *bdev, fmode_t flag) if (drop_suspend) rw_exit(&zv->zv_suspend_lock); - check_disk_change(bdev); + zfs_check_media_change(bdev); return (0);