linux: zvol: avoid heap allocation for zvol_request_sync=1
The spl_kmem_alloc showed up in some flamegraphs in a single-threaded 4k sync write workload at 85k IOPS on an Intel(R) Xeon(R) Silver 4215 CPU @ 2.50GHz. Certainly not a huge win but I believe the change is clean and easy to maintain down the road. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Matthew Ahrens <mahrens@delphix.com> Signed-off-by: Christian Schwarz <me@cschwarz.com> Closes #11666
This commit is contained in:
parent
3242b5358e
commit
e439ee83c1
|
@ -56,12 +56,32 @@ struct zvol_state_os {
|
||||||
taskq_t *zvol_taskq;
|
taskq_t *zvol_taskq;
|
||||||
static struct ida zvol_ida;
|
static struct ida zvol_ida;
|
||||||
|
|
||||||
typedef struct zv_request {
|
typedef struct zv_request_stack {
|
||||||
zvol_state_t *zv;
|
zvol_state_t *zv;
|
||||||
struct bio *bio;
|
struct bio *bio;
|
||||||
taskq_ent_t ent;
|
|
||||||
} zv_request_t;
|
} zv_request_t;
|
||||||
|
|
||||||
|
typedef struct zv_request_task {
|
||||||
|
zv_request_t zvr;
|
||||||
|
taskq_ent_t ent;
|
||||||
|
} zv_request_task_t;
|
||||||
|
|
||||||
|
static zv_request_task_t *
|
||||||
|
zv_request_task_create(zv_request_t zvr)
|
||||||
|
{
|
||||||
|
zv_request_task_t *task;
|
||||||
|
task = kmem_alloc(sizeof (zv_request_task_t), KM_SLEEP);
|
||||||
|
taskq_init_ent(&task->ent);
|
||||||
|
task->zvr = zvr;
|
||||||
|
return (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
zv_request_task_free(zv_request_task_t *task)
|
||||||
|
{
|
||||||
|
kmem_free(task, sizeof (*task));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a path, return TRUE if path is a ZVOL.
|
* Given a path, return TRUE if path is a ZVOL.
|
||||||
*/
|
*/
|
||||||
|
@ -80,9 +100,8 @@ zvol_is_zvol_impl(const char *path)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
zvol_write(void *arg)
|
zvol_write(zv_request_t *zvr)
|
||||||
{
|
{
|
||||||
zv_request_t *zvr = arg;
|
|
||||||
struct bio *bio = zvr->bio;
|
struct bio *bio = zvr->bio;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
zfs_uio_t uio;
|
zfs_uio_t uio;
|
||||||
|
@ -102,7 +121,6 @@ zvol_write(void *arg)
|
||||||
if (uio.uio_resid == 0) {
|
if (uio.uio_resid == 0) {
|
||||||
rw_exit(&zv->zv_suspend_lock);
|
rw_exit(&zv->zv_suspend_lock);
|
||||||
BIO_END_IO(bio, 0);
|
BIO_END_IO(bio, 0);
|
||||||
kmem_free(zvr, sizeof (zv_request_t));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,13 +180,19 @@ zvol_write(void *arg)
|
||||||
blk_generic_end_io_acct(q, disk, WRITE, bio, start_time);
|
blk_generic_end_io_acct(q, disk, WRITE, bio, start_time);
|
||||||
|
|
||||||
BIO_END_IO(bio, -error);
|
BIO_END_IO(bio, -error);
|
||||||
kmem_free(zvr, sizeof (zv_request_t));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
zvol_discard(void *arg)
|
zvol_write_task(void *arg)
|
||||||
|
{
|
||||||
|
zv_request_task_t *task = arg;
|
||||||
|
zvol_write(&task->zvr);
|
||||||
|
zv_request_task_free(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
zvol_discard(zv_request_t *zvr)
|
||||||
{
|
{
|
||||||
zv_request_t *zvr = arg;
|
|
||||||
struct bio *bio = zvr->bio;
|
struct bio *bio = zvr->bio;
|
||||||
zvol_state_t *zv = zvr->zv;
|
zvol_state_t *zv = zvr->zv;
|
||||||
uint64_t start = BIO_BI_SECTOR(bio) << 9;
|
uint64_t start = BIO_BI_SECTOR(bio) << 9;
|
||||||
|
@ -238,13 +262,19 @@ unlock:
|
||||||
blk_generic_end_io_acct(q, disk, WRITE, bio, start_time);
|
blk_generic_end_io_acct(q, disk, WRITE, bio, start_time);
|
||||||
|
|
||||||
BIO_END_IO(bio, -error);
|
BIO_END_IO(bio, -error);
|
||||||
kmem_free(zvr, sizeof (zv_request_t));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
zvol_read(void *arg)
|
zvol_discard_task(void *arg)
|
||||||
|
{
|
||||||
|
zv_request_task_t *task = arg;
|
||||||
|
zvol_discard(&task->zvr);
|
||||||
|
zv_request_task_free(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
zvol_read(zv_request_t *zvr)
|
||||||
{
|
{
|
||||||
zv_request_t *zvr = arg;
|
|
||||||
struct bio *bio = zvr->bio;
|
struct bio *bio = zvr->bio;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
zfs_uio_t uio;
|
zfs_uio_t uio;
|
||||||
|
@ -295,7 +325,14 @@ zvol_read(void *arg)
|
||||||
blk_generic_end_io_acct(q, disk, READ, bio, start_time);
|
blk_generic_end_io_acct(q, disk, READ, bio, start_time);
|
||||||
|
|
||||||
BIO_END_IO(bio, -error);
|
BIO_END_IO(bio, -error);
|
||||||
kmem_free(zvr, sizeof (zv_request_t));
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
zvol_read_task(void *arg)
|
||||||
|
{
|
||||||
|
zv_request_task_t *task = arg;
|
||||||
|
zvol_read(&task->zvr);
|
||||||
|
zv_request_task_free(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SUBMIT_BIO_IN_BLOCK_DEVICE_OPERATIONS
|
#ifdef HAVE_SUBMIT_BIO_IN_BLOCK_DEVICE_OPERATIONS
|
||||||
|
@ -318,7 +355,6 @@ zvol_request(struct request_queue *q, struct bio *bio)
|
||||||
uint64_t offset = BIO_BI_SECTOR(bio) << 9;
|
uint64_t offset = BIO_BI_SECTOR(bio) << 9;
|
||||||
uint64_t size = BIO_BI_SIZE(bio);
|
uint64_t size = BIO_BI_SIZE(bio);
|
||||||
int rw = bio_data_dir(bio);
|
int rw = bio_data_dir(bio);
|
||||||
zv_request_t *zvr;
|
|
||||||
|
|
||||||
if (bio_has_data(bio) && offset + size > zv->zv_volsize) {
|
if (bio_has_data(bio) && offset + size > zv->zv_volsize) {
|
||||||
printk(KERN_INFO
|
printk(KERN_INFO
|
||||||
|
@ -331,6 +367,12 @@ zvol_request(struct request_queue *q, struct bio *bio)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zv_request_t zvr = {
|
||||||
|
.zv = zv,
|
||||||
|
.bio = bio,
|
||||||
|
};
|
||||||
|
zv_request_task_t *task;
|
||||||
|
|
||||||
if (rw == WRITE) {
|
if (rw == WRITE) {
|
||||||
if (unlikely(zv->zv_flags & ZVOL_RDONLY)) {
|
if (unlikely(zv->zv_flags & ZVOL_RDONLY)) {
|
||||||
BIO_END_IO(bio, -SET_ERROR(EROFS));
|
BIO_END_IO(bio, -SET_ERROR(EROFS));
|
||||||
|
@ -361,11 +403,6 @@ zvol_request(struct request_queue *q, struct bio *bio)
|
||||||
rw_downgrade(&zv->zv_suspend_lock);
|
rw_downgrade(&zv->zv_suspend_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
zvr = kmem_alloc(sizeof (zv_request_t), KM_SLEEP);
|
|
||||||
zvr->zv = zv;
|
|
||||||
zvr->bio = bio;
|
|
||||||
taskq_init_ent(&zvr->ent);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't want this thread to be blocked waiting for i/o to
|
* We don't want this thread to be blocked waiting for i/o to
|
||||||
* complete, so we instead wait from a taskq callback. The
|
* complete, so we instead wait from a taskq callback. The
|
||||||
|
@ -398,17 +435,19 @@ zvol_request(struct request_queue *q, struct bio *bio)
|
||||||
*/
|
*/
|
||||||
if (bio_is_discard(bio) || bio_is_secure_erase(bio)) {
|
if (bio_is_discard(bio) || bio_is_secure_erase(bio)) {
|
||||||
if (zvol_request_sync) {
|
if (zvol_request_sync) {
|
||||||
zvol_discard(zvr);
|
zvol_discard(&zvr);
|
||||||
} else {
|
} else {
|
||||||
|
task = zv_request_task_create(zvr);
|
||||||
taskq_dispatch_ent(zvol_taskq,
|
taskq_dispatch_ent(zvol_taskq,
|
||||||
zvol_discard, zvr, 0, &zvr->ent);
|
zvol_discard_task, task, 0, &task->ent);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (zvol_request_sync) {
|
if (zvol_request_sync) {
|
||||||
zvol_write(zvr);
|
zvol_write(&zvr);
|
||||||
} else {
|
} else {
|
||||||
|
task = zv_request_task_create(zvr);
|
||||||
taskq_dispatch_ent(zvol_taskq,
|
taskq_dispatch_ent(zvol_taskq,
|
||||||
zvol_write, zvr, 0, &zvr->ent);
|
zvol_write_task, task, 0, &task->ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -422,19 +461,15 @@ zvol_request(struct request_queue *q, struct bio *bio)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
zvr = kmem_alloc(sizeof (zv_request_t), KM_SLEEP);
|
|
||||||
zvr->zv = zv;
|
|
||||||
zvr->bio = bio;
|
|
||||||
taskq_init_ent(&zvr->ent);
|
|
||||||
|
|
||||||
rw_enter(&zv->zv_suspend_lock, RW_READER);
|
rw_enter(&zv->zv_suspend_lock, RW_READER);
|
||||||
|
|
||||||
/* See comment in WRITE case above. */
|
/* See comment in WRITE case above. */
|
||||||
if (zvol_request_sync) {
|
if (zvol_request_sync) {
|
||||||
zvol_read(zvr);
|
zvol_read(&zvr);
|
||||||
} else {
|
} else {
|
||||||
|
task = zv_request_task_create(zvr);
|
||||||
taskq_dispatch_ent(zvol_taskq,
|
taskq_dispatch_ent(zvol_taskq,
|
||||||
zvol_read, zvr, 0, &zvr->ent);
|
zvol_read_task, task, 0, &task->ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue