Skip to content

Commit 76b39f4

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. Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Alexander Motin <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Closes openzfs#17803
1 parent 4424f86 commit 76b39f4

File tree

2 files changed

+56
-28
lines changed

2 files changed

+56
-28
lines changed

module/os/linux/zfs/zvol_os.c

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -377,16 +377,14 @@ zvol_discard(zv_request_t *zvr)
377377
}
378378

379379
/*
380-
* Align the request to volume block boundaries when a secure erase is
381-
* not required. This will prevent dnode_free_range() from zeroing out
382-
* the unaligned parts which is slow (read-modify-write) and useless
383-
* since we are not freeing any space by doing so.
380+
* Align the request to volume block boundaries. This will prevent
381+
* dnode_free_range() from zeroing out the unaligned parts which is
382+
* slow (read-modify-write) and useless since we are not freeing any
383+
* space by doing so.
384384
*/
385-
if (!io_is_secure_erase(bio, rq)) {
386-
start = P2ROUNDUP(start, zv->zv_volblocksize);
387-
end = P2ALIGN_TYPED(end, zv->zv_volblocksize, uint64_t);
388-
size = end - start;
389-
}
385+
start = P2ROUNDUP(start, zv->zv_volblocksize);
386+
end = P2ALIGN_TYPED(end, zv->zv_volblocksize, uint64_t);
387+
size = end - start;
390388

391389
if (start >= end)
392390
goto unlock;
@@ -506,6 +504,24 @@ zvol_read_task(void *arg)
506504
zv_request_task_free(task);
507505
}
508506

507+
/*
508+
* Note:
509+
*
510+
* The kernel uses different enum names for the IO opcode, depending on the
511+
* kernel version ('req_opf', 'req_op'). To sidestep this, use macros rather
512+
* than inline functions for these checks.
513+
*/
514+
/* Should this IO go down the zvol write path? */
515+
#define ZVOL_OP_IS_WRITE(op) \
516+
(op == REQ_OP_WRITE || \
517+
op == REQ_OP_FLUSH || \
518+
op == REQ_OP_DISCARD)
519+
520+
/* Is this IO type supported by zvols? */
521+
#define ZVOL_OP_IS_SUPPORTED(op) (op == REQ_OP_READ || ZVOL_OP_IS_WRITE(op))
522+
523+
/* Get the IO opcode */
524+
#define ZVOL_OP(bio, rq) (bio != NULL ? bio_op(bio) : req_op(rq))
509525

510526
/*
511527
* Process a BIO or request
@@ -525,27 +541,32 @@ zvol_request_impl(zvol_state_t *zv, struct bio *bio, struct request *rq,
525541
uint64_t size = io_size(bio, rq);
526542
int rw;
527543

528-
if (rq != NULL) {
529-
/*
530-
* Flush & trim requests go down the zvol_write codepath. Or
531-
* more specifically:
532-
*
533-
* If request is a write, or if it's op_is_sync() and not a
534-
* read, or if it's a flush, or if it's a discard, then send the
535-
* request down the write path.
536-
*/
537-
if (op_is_write(rq->cmd_flags) ||
538-
(op_is_sync(rq->cmd_flags) && req_op(rq) != REQ_OP_READ) ||
539-
req_op(rq) == REQ_OP_FLUSH ||
540-
op_is_discard(rq->cmd_flags)) {
541-
rw = WRITE;
542-
} else {
543-
rw = READ;
544-
}
544+
if (unlikely(!ZVOL_OP_IS_SUPPORTED(ZVOL_OP(bio, rq)))) {
545+
zfs_dbgmsg("Unsupported zvol %s, op=%d, flags=0x%x",
546+
rq != NULL ? "request" : "BIO",
547+
ZVOL_OP(bio, rq),
548+
rq != NULL ? rq->cmd_flags : bio->bi_opf);
549+
ASSERT(ZVOL_OP_IS_SUPPORTED(ZVOL_OP(bio, rq)));
550+
zvol_end_io(bio, rq, SET_ERROR(ENOTSUPP));
551+
goto out;
552+
}
553+
554+
if (ZVOL_OP_IS_WRITE(ZVOL_OP(bio, rq))) {
555+
rw = WRITE;
545556
} else {
546-
rw = bio_data_dir(bio);
557+
rw = READ;
547558
}
548559

560+
/*
561+
* Sanity check
562+
*
563+
* If we're a BIO, check our rw matches the kernel's
564+
* bio_data_dir(bio) rw. We need to check because we support fewer
565+
* IO operations, and want to verify that what we think are reads and
566+
* writes from those operations match what the kernel thinks.
567+
*/
568+
ASSERT(rq != NULL || rw == bio_data_dir(bio));
569+
549570
if (unlikely(zv->zv_flags & ZVOL_REMOVING)) {
550571
zvol_end_io(bio, rq, -SET_ERROR(ENXIO));
551572
goto out;
@@ -649,7 +670,7 @@ zvol_request_impl(zvol_state_t *zv, struct bio *bio, struct request *rq,
649670
* interfaces lack this functionality (they block waiting for
650671
* the i/o to complete).
651672
*/
652-
if (io_is_discard(bio, rq) || io_is_secure_erase(bio, rq)) {
673+
if (io_is_discard(bio, rq)) {
653674
if (force_sync) {
654675
zvol_discard(&zvr);
655676
} 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)