Fix sync behavior for disk vdevs

Prior to b39c22b, which was first generally available in the 0.6.5
release as b39c22b, ZoL never actually submitted synchronous read or write
requests to the Linux block layer.  This means the vdev_disk_dio_is_sync()
function had always returned false and, therefore, the completion in
dio_request_t.dr_comp was never actually used.

In b39c22b, synchronous ZIO operations were translated to synchronous
BIO requests in vdev_disk_io_start().  The follow-on commits 5592404 and
aa159af fixed several problems introduced by b39c22b.  In particular,
5592404 introduced the new flag parameter "wait" to __vdev_disk_physio()
but under ZoL, since vdev_disk_physio() is never actually used, the wait
flag was always zero so the new code had no effect other than to cause
a bug in the use of the dio_request_t.dr_comp which was fixed by aa159af.

The original rationale for introducing synchronous operations in b39c22b
was to hurry certains requests through the BIO layer which would have
otherwise been subject to its unplug timer which would increase the
latency.  This behavior of the unplug timer, however, went away during the
transition of the plug/unplug system between kernels 2.6.32 and 2.6.39.

To handle the unplug timer behavior on 2.6.32-2.6.35 kernels the
BIO_RW_UNPLUG flag is used as a hint to suppress the plugging behavior.

For kernels 2.6.36-2.6.38, the REQ_UNPLUG macro will be available and
ise used for the same purpose.

Signed-off-by: Tim Chase <tim@chase2k.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #4858
This commit is contained in:
Tim Chase 2016-07-08 10:33:01 -05:00 committed by Brian Behlendorf
parent c09af45f7b
commit 88fa992878
5 changed files with 49 additions and 40 deletions

View File

@ -0,0 +1,23 @@
dnl #
dnl # 2.6.32-2.6.35 API - The BIO_RW_UNPLUG enum can be used as a hint
dnl # to unplug the queue.
dnl #
AC_DEFUN([ZFS_AC_KERNEL_BLK_QUEUE_HAVE_BIO_RW_UNPLUG], [
AC_MSG_CHECKING([whether the BIO_RW_UNPLUG enum is available])
tmp_flags="$EXTRA_KCFLAGS"
EXTRA_KCFLAGS="${NO_UNUSED_BUT_SET_VARIABLE}"
ZFS_LINUX_TRY_COMPILE([
#include <linux/blkdev.h>
],[
extern enum bio_rw_flags rw;
rw = BIO_RW_UNPLUG;
],[
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_BLK_QUEUE_HAVE_BIO_RW_UNPLUG, 1,
[BIO_RW_UNPLUG is available])
],[
AC_MSG_RESULT(no)
])
EXTRA_KCFLAGS="$tmp_flags"
])

View File

@ -33,6 +33,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_KERNEL_BLK_QUEUE_FLUSH ZFS_AC_KERNEL_BLK_QUEUE_FLUSH
ZFS_AC_KERNEL_BLK_QUEUE_MAX_HW_SECTORS ZFS_AC_KERNEL_BLK_QUEUE_MAX_HW_SECTORS
ZFS_AC_KERNEL_BLK_QUEUE_MAX_SEGMENTS ZFS_AC_KERNEL_BLK_QUEUE_MAX_SEGMENTS
ZFS_AC_KERNEL_BLK_QUEUE_HAVE_BIO_RW_UNPLUG
ZFS_AC_KERNEL_GET_DISK_RO ZFS_AC_KERNEL_GET_DISK_RO
ZFS_AC_KERNEL_GET_GENDISK ZFS_AC_KERNEL_GET_GENDISK
ZFS_AC_KERNEL_DISCARD_GRANULARITY ZFS_AC_KERNEL_DISCARD_GRANULARITY

View File

@ -37,9 +37,11 @@ typedef struct vdev_disk {
struct block_device *vd_bdev; struct block_device *vd_bdev;
} vdev_disk_t; } vdev_disk_t;
#ifndef __linux__
extern int vdev_disk_physio(struct block_device *, caddr_t, extern int vdev_disk_physio(struct block_device *, caddr_t,
size_t, uint64_t, int, int); size_t, uint64_t, int, int);
extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **); extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **);
#endif
#endif /* _KERNEL */ #endif /* _KERNEL */
#endif /* _SYS_VDEV_DISK_H */ #endif /* _SYS_VDEV_DISK_H */

View File

@ -3863,7 +3863,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
return (0); return (0);
} }
#ifdef _KERNEL #if defined(_KERNEL) && !defined(__linux__)
/* /*
* Get the root pool information from the root disk, then import the root pool * Get the root pool information from the root disk, then import the root pool
* during the system boot up time. * during the system boot up time.
@ -4066,7 +4066,7 @@ out:
return (error); return (error);
} }
#endif #endif /* defined(_KERNEL) && !defined(__linux__) */
/* /*
* Import a non-root pool into the system. * Import a non-root pool into the system.
@ -6781,7 +6781,6 @@ EXPORT_SYMBOL(spa_open);
EXPORT_SYMBOL(spa_open_rewind); EXPORT_SYMBOL(spa_open_rewind);
EXPORT_SYMBOL(spa_get_stats); EXPORT_SYMBOL(spa_get_stats);
EXPORT_SYMBOL(spa_create); EXPORT_SYMBOL(spa_create);
EXPORT_SYMBOL(spa_import_rootpool);
EXPORT_SYMBOL(spa_import); EXPORT_SYMBOL(spa_import);
EXPORT_SYMBOL(spa_tryimport); EXPORT_SYMBOL(spa_tryimport);
EXPORT_SYMBOL(spa_destroy); EXPORT_SYMBOL(spa_destroy);

View File

@ -41,10 +41,8 @@ static void *zfs_vdev_holder = VDEV_HOLDER;
* Virtual device vector for disks. * Virtual device vector for disks.
*/ */
typedef struct dio_request { typedef struct dio_request {
struct completion dr_comp; /* Completion for sync IO */
zio_t *dr_zio; /* Parent ZIO */ zio_t *dr_zio; /* Parent ZIO */
atomic_t dr_ref; /* References */ atomic_t dr_ref; /* References */
int dr_wait; /* Wait for IO */
int dr_error; /* Bio error */ int dr_error; /* Bio error */
int dr_bio_count; /* Count of bio's */ int dr_bio_count; /* Count of bio's */
struct bio *dr_bio[0]; /* Attached bio's */ struct bio *dr_bio[0]; /* Attached bio's */
@ -363,7 +361,6 @@ vdev_disk_dio_alloc(int bio_count)
dr = kmem_zalloc(sizeof (dio_request_t) + dr = kmem_zalloc(sizeof (dio_request_t) +
sizeof (struct bio *) * bio_count, KM_SLEEP); sizeof (struct bio *) * bio_count, KM_SLEEP);
if (dr) { if (dr) {
init_completion(&dr->dr_comp);
atomic_set(&dr->dr_ref, 0); atomic_set(&dr->dr_ref, 0);
dr->dr_bio_count = bio_count; dr->dr_bio_count = bio_count;
dr->dr_error = 0; dr->dr_error = 0;
@ -426,7 +423,6 @@ BIO_END_IO_PROTO(vdev_disk_physio_completion, bio, error)
{ {
dio_request_t *dr = bio->bi_private; dio_request_t *dr = bio->bi_private;
int rc; int rc;
int wait;
if (dr->dr_error == 0) { if (dr->dr_error == 0) {
#ifdef HAVE_1ARG_BIO_END_IO_T #ifdef HAVE_1ARG_BIO_END_IO_T
@ -439,13 +435,8 @@ BIO_END_IO_PROTO(vdev_disk_physio_completion, bio, error)
#endif #endif
} }
wait = dr->dr_wait;
/* Drop reference aquired by __vdev_disk_physio */ /* Drop reference aquired by __vdev_disk_physio */
rc = vdev_disk_dio_put(dr); rc = vdev_disk_dio_put(dr);
/* Wake up synchronous waiter this is the last outstanding bio */
if (wait && rc == 1)
complete(&dr->dr_comp);
} }
static inline unsigned long static inline unsigned long
@ -527,7 +518,7 @@ vdev_submit_bio(struct bio *bio)
static int static int
__vdev_disk_physio(struct block_device *bdev, zio_t *zio, caddr_t kbuf_ptr, __vdev_disk_physio(struct block_device *bdev, zio_t *zio, caddr_t kbuf_ptr,
size_t kbuf_size, uint64_t kbuf_offset, int rw, int flags, int wait) size_t kbuf_size, uint64_t kbuf_offset, int rw, int flags)
{ {
dio_request_t *dr; dio_request_t *dr;
caddr_t bio_ptr; caddr_t bio_ptr;
@ -546,7 +537,6 @@ retry:
bio_set_flags_failfast(bdev, &flags); bio_set_flags_failfast(bdev, &flags);
dr->dr_zio = zio; dr->dr_zio = zio;
dr->dr_wait = wait;
/* /*
* When the IO size exceeds the maximum bio size for the request * When the IO size exceeds the maximum bio size for the request
@ -610,33 +600,20 @@ retry:
if (dr->dr_bio[i]) if (dr->dr_bio[i])
vdev_submit_bio(dr->dr_bio[i]); vdev_submit_bio(dr->dr_bio[i]);
/*
* On synchronous blocking requests we wait for all bio the completion
* callbacks to run. We will be woken when the last callback runs
* for this dio. We are responsible for putting the last dio_request
* reference will in turn put back the last bio references. The
* only synchronous consumer is vdev_disk_read_rootlabel() all other
* IO originating from vdev_disk_io_start() is asynchronous.
*/
if (wait) {
wait_for_completion(&dr->dr_comp);
error = dr->dr_error;
ASSERT3S(atomic_read(&dr->dr_ref), ==, 1);
}
(void) vdev_disk_dio_put(dr); (void) vdev_disk_dio_put(dr);
return (error); return (error);
} }
#ifndef __linux__
int int
vdev_disk_physio(struct block_device *bdev, caddr_t kbuf, vdev_disk_physio(struct block_device *bdev, caddr_t kbuf,
size_t size, uint64_t offset, int rw, int flags) size_t size, uint64_t offset, int rw, int flags)
{ {
bio_set_flags_failfast(bdev, &flags); bio_set_flags_failfast(bdev, &flags);
return (__vdev_disk_physio(bdev, NULL, kbuf, size, offset, rw, flags, return (__vdev_disk_physio(bdev, NULL, kbuf, size, offset, rw, flags));
1));
} }
#endif
BIO_END_IO_PROTO(vdev_disk_io_flush_completion, bio, rc) BIO_END_IO_PROTO(vdev_disk_io_flush_completion, bio, rc)
{ {
@ -688,7 +665,6 @@ vdev_disk_io_start(zio_t *zio)
{ {
vdev_t *v = zio->io_vd; vdev_t *v = zio->io_vd;
vdev_disk_t *vd = v->vdev_tsd; vdev_disk_t *vd = v->vdev_tsd;
zio_priority_t pri = zio->io_priority;
int rw, flags, error; int rw, flags, error;
switch (zio->io_type) { switch (zio->io_type) {
@ -729,18 +705,24 @@ vdev_disk_io_start(zio_t *zio)
return; return;
case ZIO_TYPE_WRITE: case ZIO_TYPE_WRITE:
rw = WRITE; rw = WRITE;
if ((pri == ZIO_PRIORITY_SYNC_WRITE) && (v->vdev_nonrot)) #if defined(HAVE_BLK_QUEUE_HAVE_BIO_RW_UNPLUG)
flags = WRITE_SYNC; flags = (1 << BIO_RW_UNPLUG);
else #elif defined(REQ_UNPLUG)
flags = REQ_UNPLUG;
#else
flags = 0; flags = 0;
#endif
break; break;
case ZIO_TYPE_READ: case ZIO_TYPE_READ:
rw = READ; rw = READ;
if ((pri == ZIO_PRIORITY_SYNC_READ) && (v->vdev_nonrot)) #if defined(HAVE_BLK_QUEUE_HAVE_BIO_RW_UNPLUG)
flags = READ_SYNC; flags = (1 << BIO_RW_UNPLUG);
else #elif defined(REQ_UNPLUG)
flags = REQ_UNPLUG;
#else
flags = 0; flags = 0;
#endif
break; break;
default: default:
@ -750,7 +732,7 @@ vdev_disk_io_start(zio_t *zio)
} }
error = __vdev_disk_physio(vd->vd_bdev, zio, zio->io_data, error = __vdev_disk_physio(vd->vd_bdev, zio, zio->io_data,
zio->io_size, zio->io_offset, rw, flags, 0); zio->io_size, zio->io_offset, rw, flags);
if (error) { if (error) {
zio->io_error = error; zio->io_error = error;
zio_interrupt(zio); zio_interrupt(zio);
@ -820,6 +802,7 @@ vdev_ops_t vdev_disk_ops = {
B_TRUE /* leaf vdev */ B_TRUE /* leaf vdev */
}; };
#ifndef __linux__
/* /*
* Given the root disk device devid or pathname, read the label from * Given the root disk device devid or pathname, read the label from
* the device, and construct a configuration nvlist. * the device, and construct a configuration nvlist.
@ -883,6 +866,7 @@ vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config)
return (0); return (0);
} }
#endif /* __linux__ */
module_param(zfs_vdev_scheduler, charp, 0644); module_param(zfs_vdev_scheduler, charp, 0644);
MODULE_PARM_DESC(zfs_vdev_scheduler, "I/O scheduler"); MODULE_PARM_DESC(zfs_vdev_scheduler, "I/O scheduler");