Skip to content

Commit 689145a

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 6a02c09 commit 689145a

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
@@ -376,16 +376,14 @@ zvol_discard(zv_request_t *zvr)
376376
}
377377

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

390388
if (start >= end)
391389
goto unlock;
@@ -505,6 +503,24 @@ zvol_read_task(void *arg)
505503
zv_request_task_free(task);
506504
}
507505

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

509525
/*
510526
* Process a BIO or request
@@ -524,27 +540,32 @@ zvol_request_impl(zvol_state_t *zv, struct bio *bio, struct request *rq,
524540
uint64_t size = io_size(bio, rq);
525541
int rw;
526542

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

559+
/*
560+
* Sanity check
561+
*
562+
* If we're a BIO, check our rw matches the kernel's
563+
* bio_data_dir(bio) rw. We need to check because we support fewer
564+
* IO operations, and want to verify that what we think are reads and
565+
* writes from those operations match what the kernel thinks.
566+
*/
567+
ASSERT(rq != NULL || rw == bio_data_dir(bio));
568+
548569
if (unlikely(zv->zv_flags & ZVOL_REMOVING)) {
549570
zvol_end_io(bio, rq, -SET_ERROR(ENXIO));
550571
goto out;
@@ -648,7 +669,7 @@ zvol_request_impl(zvol_state_t *zv, struct bio *bio, struct request *rq,
648669
* interfaces lack this functionality (they block waiting for
649670
* the i/o to complete).
650671
*/
651-
if (io_is_discard(bio, rq) || io_is_secure_erase(bio, rq)) {
672+
if (io_is_discard(bio, rq)) {
652673
if (force_sync) {
653674
zvol_discard(&zvr);
654675
} 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
@@ -40,6 +40,7 @@
4040
# 5. TRIM the first 1MB and last 2MB of the 5MB block of data.
4141
# 6. Observe 2MB of used space on the zvol
4242
# 7. Verify the trimmed regions are zero'd on the zvol
43+
# 8. Verify Secure Erase does not work on zvols (Linux only)
4344

4445
verify_runnable "global"
4546

@@ -55,6 +56,7 @@ if is_linux ; then
5556
else
5657
trimcmd='blkdiscard'
5758
fi
59+
secure_trimcmd="$trimcmd --secure"
5860
else
5961
# By default, FreeBSD 'trim' always does a dry-run. '-f' makes
6062
# it perform the actual operation.
@@ -113,6 +115,11 @@ function do_test {
113115
log_must diff $datafile1 $datafile2
114116

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

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

0 commit comments

Comments
 (0)