Skip to content

Commit adfc733

Browse files
committed
zvol: verify IO type is supported
ZVOLs don't support all block layer IO request types. Add a check for the IO types we do support. Also, remove references to io_is_secure_erase() since they are not supported on ZVOLs. Signed-off-by: Tony Hutter <hutter2@llnl.gov>
1 parent 102ff2a commit adfc733

File tree

2 files changed

+54
-28
lines changed

2 files changed

+54
-28
lines changed

module/os/linux/zfs/zvol_os.c

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -337,16 +337,14 @@ zvol_discard(zv_request_t *zvr)
337337
}
338338

339339
/*
340-
* Align the request to volume block boundaries when a secure erase is
341-
* not required. This will prevent dnode_free_range() from zeroing out
342-
* the unaligned parts which is slow (read-modify-write) and useless
343-
* since we are not freeing any space by doing so.
340+
* Align the request to volume block boundaries. This will prevent
341+
* dnode_free_range() from zeroing out the unaligned parts which is
342+
* slow (read-modify-write) and useless since we are not freeing any
343+
* space by doing so.
344344
*/
345-
if (!io_is_secure_erase(bio, rq)) {
346-
start = P2ROUNDUP(start, zv->zv_volblocksize);
347-
end = P2ALIGN_TYPED(end, zv->zv_volblocksize, uint64_t);
348-
size = end - start;
349-
}
345+
start = P2ROUNDUP(start, zv->zv_volblocksize);
346+
end = P2ALIGN_TYPED(end, zv->zv_volblocksize, uint64_t);
347+
size = end - start;
350348

351349
if (start >= end)
352350
goto unlock;
@@ -467,6 +465,24 @@ zvol_read_task(void *arg)
467465
zv_request_task_free(task);
468466
}
469467

468+
/*
469+
* Note:
470+
*
471+
* The kernel uses different enum names for the IO opcode, depending on the
472+
* kernel version ('req_opf', 'req_op'). To sidestep this, use macros rather
473+
* than inline functions for these checks.
474+
*/
475+
/* Should this IO go down the zvol write path? */
476+
#define ZVOL_OP_IS_WRITE(op) \
477+
(op == REQ_OP_WRITE || \
478+
op == REQ_OP_FLUSH || \
479+
op == REQ_OP_DISCARD)
480+
481+
/* Is this IO type supported by zvols? */
482+
#define ZVOL_OP_IS_SUPPORTED(op) (op == REQ_OP_READ || ZVOL_OP_IS_WRITE(op))
483+
484+
/* Get the IO opcode */
485+
#define ZVOL_OP(bio, rq) (bio != NULL ? bio_op(bio) : req_op(rq))
470486

471487
/*
472488
* Process a BIO or request
@@ -486,27 +502,30 @@ zvol_request_impl(zvol_state_t *zv, struct bio *bio, struct request *rq,
486502
uint64_t size = io_size(bio, rq);
487503
int rw;
488504

489-
if (rq != NULL) {
490-
/*
491-
* Flush & trim requests go down the zvol_write codepath. Or
492-
* more specifically:
493-
*
494-
* If request is a write, or if it's op_is_sync() and not a
495-
* read, or if it's a flush, or if it's a discard, then send the
496-
* request down the write path.
497-
*/
498-
if (op_is_write(rq->cmd_flags) ||
499-
(op_is_sync(rq->cmd_flags) && req_op(rq) != REQ_OP_READ) ||
500-
req_op(rq) == REQ_OP_FLUSH ||
501-
op_is_discard(rq->cmd_flags)) {
502-
rw = WRITE;
503-
} else {
504-
rw = READ;
505-
}
505+
if (unlikely(!ZVOL_OP_IS_SUPPORTED(ZVOL_OP(bio, rq)))) {
506+
zfs_dbgmsg("Unsupported zvol IO, op=%d, cmd_flags=0x%x",
507+
ZVOL_OP(bio, rq), rq->cmd_flags);
508+
ASSERT(ZVOL_OP_IS_SUPPORTED(ZVOL_OP(bio, rq)));
509+
zvol_end_io(bio, rq, SET_ERROR(ENOTSUPP));
510+
goto out;
511+
}
512+
513+
if (ZVOL_OP_IS_WRITE(ZVOL_OP(bio, rq))) {
514+
rw = WRITE;
506515
} else {
507-
rw = bio_data_dir(bio);
516+
rw = READ;
508517
}
509518

519+
/*
520+
* Sanity check
521+
*
522+
* If we're a BIO, check our rw matches the kernel's
523+
* bio_data_dir(bio) rw. We need to check because we support fewer
524+
* IO operations, and want to verify that what we think are reads and
525+
* writes from those operations match what the kernel thinks.
526+
*/
527+
ASSERT(rq != NULL || rw == bio_data_dir(bio));
528+
510529
if (unlikely(zv->zv_flags & ZVOL_REMOVING)) {
511530
zvol_end_io(bio, rq, SET_ERROR(ENXIO));
512531
goto out;
@@ -610,7 +629,7 @@ zvol_request_impl(zvol_state_t *zv, struct bio *bio, struct request *rq,
610629
* interfaces lack this functionality (they block waiting for
611630
* the i/o to complete).
612631
*/
613-
if (io_is_discard(bio, rq) || io_is_secure_erase(bio, rq)) {
632+
if (io_is_discard(bio, rq)) {
614633
if (force_sync) {
615634
zvol_discard(&zvr);
616635
} else {

tests/zfs-tests/tests/functional/zvol/zvol_misc/zvol_misc_trim.ksh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
# 5. TRIM the first 1MB and last 2MB of the 5MB block of data.
4242
# 6. Observe 2MB of used space on the zvol
4343
# 7. Verify the trimmed regions are zero'd on the zvol
44+
# 8. Verify Secure Erase does not work on zvols (Linux only)
4445

4546
verify_runnable "global"
4647

@@ -56,6 +57,7 @@ if is_linux ; then
5657
else
5758
trimcmd='blkdiscard'
5859
fi
60+
secure_trimcmd="$trimcmd --secure"
5961
else
6062
# By default, FreeBSD 'trim' always does a dry-run. '-f' makes
6163
# it perform the actual operation.
@@ -114,6 +116,11 @@ function do_test {
114116
log_must diff $datafile1 $datafile2
115117

116118
log_must rm $datafile1 $datafile2
119+
120+
# Secure erase should not work (Linux check only).
121+
if [ -n "$secure_trimcmd" ] ; then
122+
log_mustnot $secure_trimcmd $zvolpath
123+
fi
117124
}
118125

119126
log_assert "Verify that a ZFS volume can be TRIMed"

0 commit comments

Comments
 (0)