mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
zvol: Support blk-mq for better performance
Add support for the kernel's block multiqueue (blk-mq) interface in the zvol block driver. blk-mq creates multiple request queues on different CPUs rather than having a single request queue. This can improve zvol performance with multithreaded reads/writes. This implementation uses the blk-mq interfaces on 4.13 or newer kernels. Building against older kernels will fall back to the older BIO interfaces. Note that you must set the `zvol_use_blk_mq` module param to enable the blk-mq API. It is disabled by default. In addition, this commit lets the zvol blk-mq layer process whole `struct request` IOs at a time, rather than breaking them down into their individual BIOs. This reduces dbuf lock contention and overhead versus the legacy zvol submit_bio() codepath. sequential dd to one zvol, 8k volblocksize, no O_DIRECT: legacy submit_bio() 292MB/s write 453MB/s read this commit 453MB/s write 885MB/s read It also introduces a new `zvol_blk_mq_chunks_per_thread` module parameter. This parameter represents how many volblocksize'd chunks to process per each zvol thread. It can be used to tune your zvols for better read vs write performance (higher values favor write, lower favor read). Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz> Reviewed-by: Tony Nguyen <tony.nguyen@delphix.com> Signed-off-by: Tony Hutter <hutter2@llnl.gov> Closes #13148 Issue #12483
This commit is contained in:
@@ -34,6 +34,11 @@
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/msdos_fs.h> /* for SECTOR_* */
|
||||
#include <linux/bio.h>
|
||||
|
||||
#ifdef HAVE_BLK_MQ
|
||||
#include <linux/blk-mq.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_BLK_QUEUE_FLAG_SET
|
||||
static inline void
|
||||
@@ -608,4 +613,110 @@ blk_generic_alloc_queue(make_request_fn make_request, int node_id)
|
||||
}
|
||||
#endif /* !HAVE_SUBMIT_BIO_IN_BLOCK_DEVICE_OPERATIONS */
|
||||
|
||||
/*
|
||||
* All the io_*() helper functions below can operate on a bio, or a rq, but
|
||||
* not both. The older submit_bio() codepath will pass a bio, and the
|
||||
* newer blk-mq codepath will pass a rq.
|
||||
*/
|
||||
static inline int
|
||||
io_data_dir(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL) {
|
||||
if (op_is_write(req_op(rq))) {
|
||||
return (WRITE);
|
||||
} else {
|
||||
return (READ);
|
||||
}
|
||||
}
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (bio_data_dir(bio));
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_is_flush(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL)
|
||||
return (req_op(rq) == REQ_OP_FLUSH);
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (bio_is_flush(bio));
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_is_discard(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL)
|
||||
return (req_op(rq) == REQ_OP_DISCARD);
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (bio_is_discard(bio));
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_is_secure_erase(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL)
|
||||
return (req_op(rq) == REQ_OP_SECURE_ERASE);
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (bio_is_secure_erase(bio));
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_is_fua(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL)
|
||||
return (rq->cmd_flags & REQ_FUA);
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (bio_is_fua(bio));
|
||||
}
|
||||
|
||||
|
||||
static inline uint64_t
|
||||
io_offset(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL)
|
||||
return (blk_rq_pos(rq) << 9);
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (BIO_BI_SECTOR(bio) << 9);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
io_size(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL)
|
||||
return (blk_rq_bytes(rq));
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (BIO_BI_SIZE(bio));
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_has_data(struct bio *bio, struct request *rq)
|
||||
{
|
||||
#ifdef HAVE_BLK_MQ
|
||||
if (rq != NULL)
|
||||
return (bio_has_data(rq->bio));
|
||||
#else
|
||||
ASSERT3P(rq, ==, NULL);
|
||||
#endif
|
||||
return (bio_has_data(bio));
|
||||
}
|
||||
#endif /* _ZFS_BLKDEV_H */
|
||||
|
||||
@@ -69,9 +69,20 @@ typedef struct zfs_uio {
|
||||
uint16_t uio_fmode;
|
||||
uint16_t uio_extflg;
|
||||
ssize_t uio_resid;
|
||||
|
||||
size_t uio_skip;
|
||||
|
||||
struct request *rq;
|
||||
|
||||
/*
|
||||
* Used for saving rq_for_each_segment() state between calls
|
||||
* to zfs_uiomove_bvec_rq().
|
||||
*/
|
||||
struct req_iterator iter;
|
||||
struct bio_vec bv;
|
||||
} zfs_uio_t;
|
||||
|
||||
|
||||
#define zfs_uio_segflg(u) (u)->uio_segflg
|
||||
#define zfs_uio_offset(u) (u)->uio_loffset
|
||||
#define zfs_uio_resid(u) (u)->uio_resid
|
||||
@@ -116,17 +127,33 @@ zfs_uio_iovec_init(zfs_uio_t *uio, const struct iovec *iov,
|
||||
}
|
||||
|
||||
static inline void
|
||||
zfs_uio_bvec_init(zfs_uio_t *uio, struct bio *bio)
|
||||
zfs_uio_bvec_init(zfs_uio_t *uio, struct bio *bio, struct request *rq)
|
||||
{
|
||||
uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)];
|
||||
uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio);
|
||||
uio->uio_loffset = BIO_BI_SECTOR(bio) << 9;
|
||||
/* Either bio or rq will be set, but not both */
|
||||
ASSERT3P(uio, !=, bio);
|
||||
|
||||
if (bio) {
|
||||
uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio);
|
||||
uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)];
|
||||
} else {
|
||||
uio->uio_bvec = NULL;
|
||||
uio->uio_iovcnt = 0;
|
||||
memset(&uio->iter, 0, sizeof (uio->iter));
|
||||
}
|
||||
|
||||
uio->uio_loffset = io_offset(bio, rq);
|
||||
uio->uio_segflg = UIO_BVEC;
|
||||
uio->uio_fault_disable = B_FALSE;
|
||||
uio->uio_fmode = 0;
|
||||
uio->uio_extflg = 0;
|
||||
uio->uio_resid = BIO_BI_SIZE(bio);
|
||||
uio->uio_skip = BIO_BI_SKIP(bio);
|
||||
uio->uio_resid = io_size(bio, rq);
|
||||
if (bio) {
|
||||
uio->uio_skip = BIO_BI_SKIP(bio);
|
||||
} else {
|
||||
uio->uio_skip = 0;
|
||||
}
|
||||
|
||||
uio->rq = rq;
|
||||
}
|
||||
|
||||
#if defined(HAVE_VFS_IOV_ITER)
|
||||
|
||||
Reference in New Issue
Block a user