1#!/bin/ksh -p 2# SPDX-License-Identifier: CDDL-1.0 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License (the "License"). 7# You may not use this file except in compliance with the License. 8# 9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10# or https://opensource.org/licenses/CDDL-1.0. 11# See the License for the specific language governing permissions 12# and limitations under the License. 13# 14# When distributing Covered Code, include this CDDL HEADER in each 15# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16# If applicable, add the following below this CDDL HEADER, with the 17# fields enclosed by brackets "[]" replaced with your own identifying 18# information: Portions Copyright [yyyy] [name of copyright owner] 19# 20# CDDL HEADER END 21# 22 23# 24# Copyright (c) 2023, Klara Inc. 25# 26 27# DESCRIPTION: 28# Verify that new entries are not added to the DDT when dedup_table_quota has 29# been exceeded. 30# 31# STRATEGY: 32# 1. Create a pool with dedup=on 33# 2. Set threshold for on-disk DDT via dedup_table_quota 34# 3. Verify the threshold is exceeded after zpool sync 35# 4. Verify no new entries are added after subsequent sync's 36# 5. Remove all but one entry from DDT 37# 6. Verify new entries are added to DDT 38# 39 40. $STF_SUITE/include/libtest.shlib 41. $STF_SUITE/tests/functional/events/events_common.kshlib 42 43verify_runnable "both" 44 45log_assert "DDT quota is enforced" 46 47MOUNTDIR="$TEST_BASE_DIR/dedup_mount" 48FILEPATH="$MOUNTDIR/dedup_file" 49VDEV_GENERAL="$TEST_BASE_DIR/vdevfile.general.$$" 50VDEV_DEDUP="$TEST_BASE_DIR/vdevfile.dedup.$$" 51POOL="dedup_pool" 52 53# we set the dedup log txg interval to 1, to get a log flush every txg, 54# effectively disabling the log. without this it's hard to predict when and 55# where things appear on-disk 56log_must save_tunable DEDUP_LOG_TXG_MAX 57log_must set_tunable32 DEDUP_LOG_TXG_MAX 1 58log_must save_tunable DEDUP_LOG_FLUSH_ENTRIES_MIN 59log_must set_tunable32 DEDUP_LOG_FLUSH_ENTRIES_MIN 100000 60 61function cleanup 62{ 63 if poolexists $POOL ; then 64 destroy_pool $POOL 65 fi 66 log_must rm -fd $VDEV_GENERAL $VDEV_DEDUP $MOUNTDIR 67 log_must restore_tunable DEDUP_LOG_TXG_MAX 68 log_must restore_tunable DEDUP_LOG_FLUSH_ENTRIES_MIN 69} 70 71 72function do_clean 73{ 74 log_must destroy_pool $POOL 75 log_must rm -fd $VDEV_GENERAL $VDEV_DEDUP $MOUNTDIR 76} 77 78function do_setup 79{ 80 log_must truncate -s 5G $VDEV_GENERAL 81 # Use 'xattr=sa' to prevent selinux xattrs influencing our accounting 82 log_must zpool create -f -O xattr=sa -m $MOUNTDIR $POOL $VDEV_GENERAL 83 log_must zfs set compression=off dedup=on $POOL 84} 85 86function dedup_table_size 87{ 88 get_pool_prop dedup_table_size $POOL 89} 90 91function dedup_table_quota 92{ 93 get_pool_prop dedup_table_quota $POOL 94} 95 96function ddt_entries 97{ 98 typeset -i entries=$(zpool status -D $POOL | \ 99 grep "dedup: DDT entries" | awk '{print $4}') 100 101 echo ${entries} 102} 103 104function ddt_add_entry 105{ 106 count=$1 107 offset=$2 108 expand=$3 109 110 if [ -z "$offset" ]; then 111 offset=1 112 fi 113 114 for i in {$offset..$count}; do 115 echo "$i" > $MOUNTDIR/dedup-$i.txt 116 done 117 log_must sync_pool $POOL 118 119 log_note range $offset - $(( count + offset - 1 )) 120 log_note ddt_add_entry got $(ddt_entries) 121} 122 123# Create 6000 entries over three syncs 124function ddt_nolimit 125{ 126 do_setup 127 128 log_note base ddt entries is $(ddt_entries) 129 130 ddt_add_entry 1 131 ddt_add_entry 100 132 ddt_add_entry 101 5000 133 ddt_add_entry 5001 6000 134 135 log_must test $(ddt_entries) -eq 6000 136 137 do_clean 138} 139 140function ddt_limit 141{ 142 do_setup 143 144 log_note base ddt entries is $(ddt_entries) 145 146 log_must zpool set dedup_table_quota=32768 $POOL 147 ddt_add_entry 100 148 149 # it's possible to exceed dedup_table_quota over a single transaction, 150 # ensure that the threshold has been exceeded 151 cursize=$(dedup_table_size) 152 log_must test $cursize -gt $(dedup_table_quota) 153 154 # count the entries we have 155 log_must test $(ddt_entries) -ge 100 156 157 # attempt to add new entries 158 ddt_add_entry 101 200 159 log_must test $(ddt_entries) -eq 100 160 log_must test $cursize -eq $(dedup_table_size) 161 162 # remove all but one entry 163 for i in {2..100}; do 164 rm $MOUNTDIR/dedup-$i.txt 165 done 166 log_must sync_pool $POOL 167 168 log_must test $(ddt_entries) -eq 1 169 log_must test $cursize -gt $(dedup_table_size) 170 cursize=$(dedup_table_size) 171 172 log_must zpool set dedup_table_quota=none $POOL 173 174 # create more entries 175 zpool status -D $POOL 176 ddt_add_entry 101 200 177 log_must sync_pool $POOL 178 179 log_must test $(ddt_entries) -eq 101 180 log_must test $cursize -lt $(dedup_table_size) 181 182 do_clean 183} 184 185function ddt_dedup_vdev_limit 186{ 187 do_setup 188 189 # add a dedicated dedup/special VDEV and enable an automatic quota 190 if (( RANDOM % 2 == 0 )) ; then 191 class="special" 192 size="200M" 193 else 194 class="dedup" 195 size="100M" 196 fi 197 log_must truncate -s $size $VDEV_DEDUP 198 log_must zpool add $POOL $class $VDEV_DEDUP 199 log_must zpool set dedup_table_quota=auto $POOL 200 201 log_must zfs set recordsize=1K $POOL 202 203 # Generate a working set to fill up the dedup/special allocation class 204 for i in {0..63}; do 205 log_must dd if=/dev/urandom of=$MOUNTDIR/file${i} bs=1M count=16 206 log_must sync_pool $POOL 207 done 208 209 zpool status -D $POOL 210 zpool list -v $POOL 211 echo DDT size $(dedup_table_size), with $(ddt_entries) entries 212 213 # 214 # With no DDT quota in place, the above workload will produce up to 215 # 1M of entries by using space in the normal class. With a quota, it 216 # should be well under 500,000. However, logged entries are hard to 217 # account for because they can appear on both logs, and can also 218 # represent an eventual removal. This isn't easily visible from 219 # outside, and even internally can result in going slightly over quota. 220 # For here, we just set the entry count a little higher than what we 221 # expect to allow for some instability. 222 # 223 log_must test $(ddt_entries) -le 650000 224 225 do_clean 226} 227 228log_onexit cleanup 229 230ddt_limit 231ddt_nolimit 232ddt_dedup_vdev_limit 233 234log_pass "DDT quota is enforced" 235