xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/tests/functional/stat/statx_dioalign.ksh (revision d9497217456002b0ddad3cd319570d0b098daa29)
1#!/bin/ksh -p
2# SPDX-License-Identifier: CDDL-1.0
3#
4# CDDL HEADER START
5#
6# The contents of this file are subject to the terms of the
7# Common Development and Distribution License (the "License").
8# You may not use this file except in compliance with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or https://opensource.org/licenses/CDDL-1.0.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23
24#
25# Copyright (c) 2025, Rob Norris <robn@despairlabs.com>
26#
27
28#
29# Uses the statx helper to test the results of the STATX_DIOALIGN request as we
30# manipulate DIO enable, dataset recordsize and file size and structure.
31#
32
33. $STF_SUITE/include/libtest.shlib
34
35verify_runnable "both"
36
37if ! is_linux ; then
38	log_unsupported "statx(2) only available on Linux"
39fi
40
41if [[ $(linux_version) -lt $(linux_version "6.1") ]] ; then
42	log_unsupported "STATX_DIOALIGN not available before Linux 6.1"
43fi
44
45CLAIM="STATX_DIOALIGN returns useful values when Direct IO is available."
46
47TESTDS=${TESTPOOL}/${TESTFS}
48TESTFILE=${TESTDIR}/${TESTFILE0}
49
50log_must save_tunable DIO_ENABLED
51log_must save_tunable DIO_STRICT
52typeset recordsize_saved=$(get_prop recordsize $TESTDS)
53typeset direct_saved=$(get_prop direct $TESTDS)
54
55function cleanup
56{
57	rm -f ${TESTFILE}
58	zfs set recordsize=$recordsize_saved $TESTDS
59	zfs set direct=$direct_saved $TESTDS
60	restore_tunable DIO_ENABLED
61	restore_tunable DIO_STRICT
62}
63log_onexit cleanup
64
65# assert_dioalign <file> <memalign> <ioalign>
66function assert_dioalign
67{
68	typeset file=$1
69	typeset -i memalign=$2
70	typeset -i ioalign=$3
71
72	typeset -a v=($(statx dioalign $file | cut -f2- -d' '))
73	log_note "statx dioalign returned: $file: mem=${v[0]} io=${v[1]}"
74	log_must [ ${v[0]} -eq $memalign -a ${v[1]} -eq $ioalign ]
75}
76
77# assert_dioalign_failed <file>
78function assert_dioalign_failed
79{
80	typeset file=$1
81	log_mustnot statx dioalign $file
82}
83
84log_assert $CLAIM
85
86# The mem alignment will always be PAGE_SIZE, so we need to know what that is.
87typeset -i PAGE_SIZE=$(getconf PAGE_SIZE)
88
89# Set recordsize to 128K, and make a 64K file (so only one block) for the
90# sizing tests below.
91log_must zfs set recordsize=128K $TESTDS
92log_must rm -f $TESTFILE
93log_must stride_dd -i /dev/urandom -o $TESTFILE -b 65536 -c 1
94log_must zpool sync
95
96# when DIO is disabled via tunable, statx will not return the dioalign result
97# and the program fails
98log_must set_tunable32 DIO_ENABLED 0
99
100for d in disabled standard always ; do
101    log_must zfs set direct=$d $TESTDS
102    assert_dioalign_failed $TESTFILE
103done
104
105# when DIO is enabled via tunable, behaviour is dependent on the direct=
106# property.
107log_must set_tunable32 DIO_ENABLED 1
108
109# when DIO is disabled via property, statx fails
110log_must zfs set direct=disabled $TESTDS
111assert_dioalign_failed $TESTFILE
112
113# when DIO is enabled, the result should be mem=pagesize, io=recordsize
114for d in standard always ; do
115    log_must zfs set direct=$d $TESTDS
116    assert_dioalign $TESTFILE $PAGE_SIZE 131072
117done
118
119# The IO size is the file's blocksize, unless it is in its first block and
120# could grow to the recordsize. Our test file is currently a single 64K block,
121# so any recordsize equal or larger than that should be used for the alignment.
122for krs in 64 128 256 512 ; do
123    typeset -i rs=$((krs * 1024))
124    log_must zfs set recordsize=$rs $TESTDS
125    for d in standard always ; do
126	log_must zfs set direct=$d $TESTDS
127	assert_dioalign $TESTFILE $PAGE_SIZE $rs
128    done
129done
130
131# If the recordsize is smaller than the block size, then the file's block size
132# will always be used.
133for krs in 4 8 16 32 64 ; do
134    typeset -i rs=$((krs * 1024))
135    log_must zfs set recordsize=$rs $TESTDS
136    for d in standard always ; do
137	log_must zfs set direct=$d $TESTDS
138	assert_dioalign $TESTFILE $PAGE_SIZE 65536
139    done
140done
141
142# Now we extend the file into its second block. This effectively locks in its
143# block size, which will always be returned regardless of recordsize changes.
144log_must zfs set recordsize=128K $TESTDS
145log_must stride_dd -i /dev/urandom -o $TESTFILE -b 196608 -c 1
146log_must zpool sync
147
148# Confirm that no matter how we change the recordsize, the alignment remains at
149# the block size.
150for krs in 4 8 16 32 64 128 256 512 ; do
151    typeset -i rs=$((krs * 1024))
152    log_must zfs set recordsize=$rs $TESTDS
153    for d in standard always ; do
154	log_must zfs set direct=$d $TESTDS
155	assert_dioalign $TESTFILE $PAGE_SIZE 131072
156    done
157done
158
159# reset for write tests
160log_must set_tunable32 DIO_STRICT 1
161log_must zfs set recordsize=16K $TESTDS
162log_must zfs set direct=standard $TESTDS
163
164# create an empty file, and fetch its alignment (which we know, so just test
165# for it). then, do some O_DIRECT writes with that alignment. they should
166# succeed.
167log_must rm -f $TESTFILE
168log_must touch $TESTFILE
169log_must zpool sync
170assert_dioalign $TESTFILE $PAGE_SIZE 16384
171log_must stride_dd -i /dev/urandom -o $TESTFILE -b 16384 -c 16 -D
172
173# same again, but writing with incorrect alignment, which should fail.
174log_must rm -f $TESTFILE
175log_must touch $TESTFILE
176log_must zpool sync
177assert_dioalign $TESTFILE $PAGE_SIZE 16384
178log_mustnot stride_dd -i /dev/urandom -o $TESTFILE -b 1024 -c 256 -D
179
180# same again, but without strict, which should succeed.
181log_must set_tunable32 DIO_STRICT 0
182log_must rm -f $TESTFILE
183log_must touch $TESTFILE
184log_must zpool sync
185assert_dioalign $TESTFILE $PAGE_SIZE 16384
186log_must stride_dd -i /dev/urandom -o $TESTFILE -b 1024 -c 256 -D
187
188log_pass $CLAIM
189