1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or https://opensource.org/licenses/CDDL-1.0.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25#
26
27#
28# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
29#
30
31. $STF_SUITE/tests/functional/reservation/reservation.cfg
32
33#
34# Function to set the reservation property of a dataset to
35# 'none' and verify that it is correctly set using both the
36# "normal" 'zfs get reservation' and the '-p' option which
37# gives a numerical value.
38#
39function zero_reservation
40{
41	dataset=$1
42
43	log_must zfs set reservation=none $dataset
44
45	log_must eval 'resv_val="$(zfs get -Ho value reservation $dataset)"'
46	log_must [ $resv_val = "none" ]
47
48	log_must eval 'resv_val="$(zfs get -pHo value reservation $dataset)"'
49	log_must [ $resv_val -eq 0 ]
50
51	return 0
52}
53
54#
55# Utility function to see if two values are within a certain specified
56# limit of each other. Used primarily to check that a dataset's parent
57# is correctly accounting for space used/available. Need this function as
58# currently there is some slop in the way space is accounted (i.e. can't
59# do a direct comparison).
60#
61function within_limits
62{
63	typeset valA=$1
64	typeset valB=$2
65	typeset delta=$3
66
67	if ((valA <= valB)); then
68		if (((valB - valA) <= delta)); then
69			return 0
70		fi
71	elif ((valB <= valA)); then
72		if (((valA - valB) <= delta)); then
73			return 0
74		fi
75	fi
76
77	return 1
78}
79
80#
81# Function to create and mount multiple filesystems. The filesystem
82# will be named according to the name specified with a suffix value
83# taken from the loop counter.
84#
85function create_multiple_fs # num_fs base_fs_name base_mnt_name
86{
87	typeset -i iter=0
88	typeset -i count=$1
89	typeset FS_NAME=$2
90	typeset MNT_NAME=$3
91
92	while  (($iter < $count)); do
93		log_must zfs create ${FS_NAME}$iter
94		log_must zfs set mountpoint=${MNT_NAME}$iter ${FS_NAME}$iter
95		((iter = iter + 1))
96	done
97}
98
99#
100# This function compute the largest volume size which is multiple of volume
101# block size (default 16K) and not greater than the largest expected volsize.
102#
103# $1 The largest expected volume size.
104# $2 The volume block size
105#
106function floor_volsize #<largest_volsize> [volblksize]
107{
108	typeset largest_volsize=$1
109	typeset volblksize=${2:-16384}
110
111	if ((largest_volsize < volblksize)); then
112		log_fail "The largest_volsize must be greater than volblksize."
113	fi
114	typeset real_volsize
115	typeset n
116
117	((n = largest_volsize / volblksize))
118	((largest_volsize = volblksize * n))
119
120	print $largest_volsize
121}
122
123#
124# This function is a copy of a function by the same name in libzfs_dataset.c
125# Its purpose is to reserve additional space for volume metadata so volumes
126# don't unexpectedly run out of room.
127#
128# Note: This function can be used to do an estimate for a volume that has not
129# yet been created. In this case, $vol is not a volume, but rather a pool in
130# which a volume is going to be created. In this case, use default properties.
131#
132function volsize_to_reservation
133{
134	typeset vol=$1
135	typeset volsize=$2
136
137	typeset -i DN_MAX_INDBLKSHIFT=17
138	typeset -i SPA_BLKPTRSHIFT=7
139	typeset -i SPA_DVAS_PER_BP=3
140
141	typeset DNODES_PER_LEVEL_SHIFT=$((DN_MAX_INDBLKSHIFT - \
142	    SPA_BLKPTRSHIFT))
143	typeset DNODES_PER_LEVEL=$((1 << $DNODES_PER_LEVEL_SHIFT))
144
145	if ds_is_volume $vol; then
146		typeset ncopies=$(get_prop copies $vol)
147		typeset volblocksize=$(get_prop volblocksize $vol)
148	else
149		typeset ncopies=1
150		typeset volblocksize=16384
151	fi
152	typeset nblocks=$((volsize / volblocksize))
153
154	typeset numdb=7
155	while ((nblocks > 1)); do
156		((nblocks += DNODES_PER_LEVEL - 1))
157		((nblocks /= DNODES_PER_LEVEL))
158		((numdb += nblocks))
159	done
160
161	((numdb *= SPA_DVAS_PER_BP < ncopies + 1 ? SPA_DVAS_PER_BP : \
162	    ncopies + 1))
163	((volsize *= ncopies))
164	((numdb *= 1 << DN_MAX_INDBLKSHIFT))
165	((volsize += numdb))
166	echo $volsize
167}
168
169#
170# This function takes a pool name as an argument, and returns the largest (give
171# or take some slop) -V value that can be used to create a volume in that pool.
172# This is necessary because during volume creation, a reservation is created
173# that will be larger than the value specified with -V, and potentially larger
174# than the available space in the pool. See volsize_to_reservation().
175#
176function largest_volsize_from_pool
177{
178	typeset pool=$1
179	typeset poolsize=$(get_prop available $pool)
180	typeset volsize=$poolsize
181	typeset nvolsize
182
183	while :; do
184		# knock 50M off the volsize each time through
185		((volsize -= 50 * 1024 * 1024))
186		nvolsize=$(volsize_to_reservation $pool $volsize)
187		nvolsize=$(floor_volsize $nvolsize)
188		((nvolsize < poolsize)) && break
189	done
190	echo $volsize
191}
192