| # |
| # This file and its contents are supplied under the terms of the |
| # Common Development and Distribution License ("CDDL"), version 1.0. |
| # You may only use this file in accordance with the terms of version |
| # 1.0 of the CDDL. |
| # |
| # A full copy of the text of the CDDL should have accompanied this |
| # source. A copy of the CDDL is also available via the Internet at |
| # http://www.illumos.org/license/CDDL. |
| # |
| |
| # |
| # Copyright (c) 2017, 2018 by Delphix. All rights reserved. |
| # |
| |
| . $STF_SUITE/include/libtest.shlib |
| . $STF_SUITE/tests/functional/removal/removal.kshlib |
| |
| # |
| # In general all the tests related to the pool checkpoint can |
| # be divided into two categories. TESTS that verify features |
| # provided by the checkpoint (e.g. checkpoint_rewind) and tests |
| # that stress-test the checkpoint (e.g. checkpoint_big_rewind). |
| # |
| # For the first group we don't really care about the size of |
| # the pool or the individual file sizes within the filesystems. |
| # This is why these tests run directly on pools that use a |
| # "real disk vdev" (meaning not a file based one). These tests |
| # use the $TESTPOOL pool that is created on top of $TESTDISK. |
| # This pool is referred to as the "test pool" and thus all |
| # the tests of this group use the testpool-related functions of |
| # this file (not the nested_pools ones). |
| # |
| # For the second group we generally try to bring the pool to its |
| # limits by increasing fragmentation, filling all allocatable |
| # space, attempting to use vdevs that the checkpoint spacemap |
| # cannot represent, etc. For these tests we need to control |
| # almost all parameters of the pool and the vdevs that back it |
| # so we create them based on file-based vdevs that we carefully |
| # create within the $TESTPOOL pool. So most of these tests, in |
| # order to create this nested pool sctructure, generally start |
| # like this: |
| # 1] We create the test pool ($TESTPOOL). |
| # 2] We create a filesystem and we populate it with files of |
| # some predetermined size. |
| # 3] We use those files as vdevs for the pool that the test |
| # will use ($NESTEDPOOL). |
| # 4] Go on and let the test run and operate on $NESTEDPOOL. |
| # |
| |
| # |
| # These disks are used to back $TESTPOOL |
| # |
| TESTDISK="$(echo $DISKS | cut -d' ' -f1)" |
| EXTRATESTDISK="$(echo $DISKS | cut -d' ' -f2)" |
| |
| FS0=$TESTPOOL/$TESTFS |
| FS1=$TESTPOOL/$TESTFS1 |
| FS2=$TESTPOOL/$TESTFS2 |
| |
| FS0FILE=/$FS0/$TESTFILE0 |
| FS1FILE=/$FS1/$TESTFILE1 |
| FS2FILE=/$FS2/$TESTFILE2 |
| |
| # |
| # The following are created within $TESTPOOL and |
| # will be used to back $NESTEDPOOL |
| # |
| DISKFS=$TESTPOOL/disks |
| FILEDISKDIR=/$DISKFS |
| FILEDISK1=/$DISKFS/dsk1 |
| FILEDISK2=/$DISKFS/dsk2 |
| FILEDISKS="$FILEDISK1 $FILEDISK2" |
| |
| # |
| # $NESTEDPOOL related variables |
| # |
| NESTEDPOOL=nestedpool |
| NESTEDFS0=$NESTEDPOOL/$TESTFS |
| NESTEDFS1=$NESTEDPOOL/$TESTFS1 |
| NESTEDFS2=$NESTEDPOOL/$TESTFS2 |
| NESTEDFS0FILE=/$NESTEDFS0/$TESTFILE0 |
| NESTEDFS1FILE=/$NESTEDFS1/$TESTFILE1 |
| NESTEDFS2FILE=/$NESTEDFS2/$TESTFILE2 |
| |
| # |
| # In the tests that stress-test the pool (second category |
| # mentioned above), there exist some that need to bring |
| # fragmentation at high percentages in a relatively short |
| # period of time. In order to do that we set the following |
| # parameters: |
| # |
| # * We use two disks of 1G each, to create a pool of size 2G. |
| # The point is that 2G is not small nor large, and we also |
| # want to have 2 disks to introduce indirect vdevs on our |
| # setup. |
| # * We enable compression and set the record size of all |
| # filesystems to 8K. The point of compression is to |
| # ensure that we are not filling up the whole pool (that's |
| # what checkpoint_capacity is for), and the specific |
| # record size is set to match the block size of randwritecomp |
| # which is used to increase fragmentation by writing on |
| # files. |
| # * We always have 2 big files present of 512M each, which |
| # should account for 40%~50% capacity by the end of each |
| # test with fragmentation around 50~60%. |
| # * At each file we attempt to do enough random writes to |
| # touch every offset twice on average. |
| # |
| # Note that the amount of random writes per files are based |
| # on the following calculation: |
| # |
| # ((512M / 8K) * 3) * 2 = ~400000 |
| # |
| # Given that the file is 512M and one write is 8K, we would |
| # need (512M / 8K) writes to go through the whole file. |
| # Assuming though that each write has a compression ratio of |
| # 3, then we want 3 times that to cover the same amount of |
| # space. Finally, we multiply that by 2 since our goal is to |
| # touch each offset twice on average. |
| # |
| # Examples of those tests are checkpoint_big_rewind and |
| # checkpoint_discard_busy. |
| # |
| FILEDISKSIZE=1g |
| DISKSIZE=1g |
| BIGFILESIZE=512M |
| RANDOMWRITES=400000 |
| |
| |
| # |
| # Assumes create_test_pool has been called beforehand. |
| # |
| function setup_nested_pool |
| { |
| log_must zfs create $DISKFS |
| |
| log_must truncate -s $DISKSIZE $FILEDISK1 |
| log_must truncate -s $DISKSIZE $FILEDISK2 |
| |
| log_must zpool create -O sync=disabled $NESTEDPOOL $FILEDISKS |
| } |
| |
| function setup_test_pool |
| { |
| log_must zpool create -O sync=disabled $TESTPOOL "$TESTDISK" |
| } |
| |
| function setup_nested_pools |
| { |
| setup_test_pool |
| setup_nested_pool |
| } |
| |
| function cleanup_nested_pool |
| { |
| if poolexists $NESTEDPOOL; then |
| log_must zpool destroy $NESTEDPOOL |
| fi |
| |
| log_must rm -f $FILEDISKS |
| } |
| |
| function cleanup_test_pool |
| { |
| if poolexists $TESTPOOL; then |
| log_must zpool destroy $TESTPOOL |
| fi |
| |
| # |
| # We always clear the labels of all disks |
| # between tests so imports from zpool or |
| # or zdb do not get confused with leftover |
| # data from old pools. |
| # |
| for disk in $DISKS; do |
| zpool labelclear -f $disk |
| done |
| } |
| |
| function cleanup_nested_pools |
| { |
| cleanup_nested_pool |
| cleanup_test_pool |
| } |
| |
| # |
| # Remove and re-add each vdev to ensure that data is |
| # moved between disks and indirect mappings are created |
| # |
| function introduce_indirection |
| { |
| for disk in ${FILEDISKS[@]}; do |
| log_must zpool remove $NESTEDPOOL $disk |
| log_must wait_for_removal $NESTEDPOOL |
| log_mustnot vdevs_in_pool $NESTEDPOOL $disk |
| log_must zpool add $NESTEDPOOL $disk |
| done |
| } |
| |
| FILECONTENTS0="Can't wait to be checkpointed!" |
| FILECONTENTS1="Can't wait to be checkpointed too!" |
| NEWFILECONTENTS0="I survived after the checkpoint!" |
| NEWFILECONTENTS2="I was born after the checkpoint!" |
| |
| function populate_test_pool |
| { |
| log_must zfs create -o compression=lz4 -o recordsize=8k $FS0 |
| log_must zfs create -o compression=lz4 -o recordsize=8k $FS1 |
| |
| echo $FILECONTENTS0 > $FS0FILE |
| echo $FILECONTENTS1 > $FS1FILE |
| } |
| |
| function populate_nested_pool |
| { |
| log_must zfs create -o compression=lz4 -o recordsize=8k $NESTEDFS0 |
| log_must zfs create -o compression=lz4 -o recordsize=8k $NESTEDFS1 |
| |
| echo $FILECONTENTS0 > $NESTEDFS0FILE |
| echo $FILECONTENTS1 > $NESTEDFS1FILE |
| } |
| |
| function test_verify_pre_checkpoint_state |
| { |
| log_must zfs list $FS0 |
| log_must zfs list $FS1 |
| log_must [ "$(<$FS0FILE)" = "$FILECONTENTS0" ] |
| log_must [ "$(<$FS1FILE)" = "$FILECONTENTS1" ] |
| |
| # |
| # If we've opened the checkpointed state of the |
| # pool as read-only without rewinding on-disk we |
| # can't really use zdb on it. |
| # |
| if [[ "$1" != "ro-check" ]] ; then |
| log_must zdb $TESTPOOL |
| fi |
| |
| # |
| # Ensure post-checkpoint state is not present |
| # |
| log_mustnot zfs list $FS2 |
| log_mustnot [ "$(<$FS0FILE)" = "$NEWFILECONTENTS0" ] |
| } |
| |
| function nested_verify_pre_checkpoint_state |
| { |
| log_must zfs list $NESTEDFS0 |
| log_must zfs list $NESTEDFS1 |
| log_must [ "$(<$NESTEDFS0FILE)" = "$FILECONTENTS0" ] |
| log_must [ "$(<$NESTEDFS1FILE)" = "$FILECONTENTS1" ] |
| |
| # |
| # If we've opened the checkpointed state of the |
| # pool as read-only without rewinding on-disk we |
| # can't really use zdb on it. |
| # |
| if [[ "$1" != "ro-check" ]] ; then |
| log_must zdb $NESTEDPOOL |
| fi |
| |
| # |
| # Ensure post-checkpoint state is not present |
| # |
| log_mustnot zfs list $NESTEDFS2 |
| log_mustnot [ "$(<$NESTEDFS0FILE)" = "$NEWFILECONTENTS0" ] |
| } |
| |
| function test_change_state_after_checkpoint |
| { |
| log_must zfs destroy $FS1 |
| log_must zfs create -o compression=lz4 -o recordsize=8k $FS2 |
| |
| echo $NEWFILECONTENTS0 > $FS0FILE |
| echo $NEWFILECONTENTS2 > $FS2FILE |
| } |
| |
| function nested_change_state_after_checkpoint |
| { |
| log_must zfs destroy $NESTEDFS1 |
| log_must zfs create -o compression=lz4 -o recordsize=8k $NESTEDFS2 |
| |
| echo $NEWFILECONTENTS0 > $NESTEDFS0FILE |
| echo $NEWFILECONTENTS2 > $NESTEDFS2FILE |
| } |
| |
| function test_verify_post_checkpoint_state |
| { |
| log_must zfs list $FS0 |
| log_must zfs list $FS2 |
| log_must [ "$(<$FS0FILE)" = "$NEWFILECONTENTS0" ] |
| log_must [ "$(<$FS2FILE)" = "$NEWFILECONTENTS2" ] |
| |
| log_must zdb $TESTPOOL |
| |
| # |
| # Ensure pre-checkpointed state that was removed post-checkpoint |
| # is not present |
| # |
| log_mustnot zfs list $FS1 |
| log_mustnot [ "$(<$FS0FILE)" = "$FILECONTENTS0" ] |
| } |
| |
| function fragment_before_checkpoint |
| { |
| populate_nested_pool |
| log_must mkfile -n $BIGFILESIZE $NESTEDFS0FILE |
| log_must mkfile -n $BIGFILESIZE $NESTEDFS1FILE |
| log_must randwritecomp $NESTEDFS0FILE $RANDOMWRITES |
| log_must randwritecomp $NESTEDFS1FILE $RANDOMWRITES |
| |
| # |
| # Display fragmentation on test log |
| # |
| log_must zpool list -v |
| } |
| |
| function fragment_after_checkpoint_and_verify |
| { |
| log_must zfs destroy $NESTEDFS1 |
| log_must zfs create -o compression=lz4 -o recordsize=8k $NESTEDFS2 |
| log_must mkfile -n $BIGFILESIZE $NESTEDFS2FILE |
| log_must randwritecomp $NESTEDFS0FILE $RANDOMWRITES |
| log_must randwritecomp $NESTEDFS2FILE $RANDOMWRITES |
| |
| # |
| # Display fragmentation on test log |
| # |
| log_must zpool list -v |
| |
| # |
| # Typically we would just run zdb at this point and things |
| # would be fine. Unfortunately, if there is still any |
| # background I/O in the pool the zdb command can fail with |
| # checksum errors temporarily. |
| # |
| # Export the pool when running zdb so the pool is idle and |
| # the verification results are consistent. |
| # |
| log_must zpool export $NESTEDPOOL |
| log_must zdb -e -p $FILEDISKDIR $NESTEDPOOL |
| log_must zdb -e -p $FILEDISKDIR -kc $NESTEDPOOL |
| log_must zpool import -d $FILEDISKDIR $NESTEDPOOL |
| } |
| |
| function wait_discard_finish |
| { |
| typeset pool="$1" |
| |
| typeset status |
| status=$(zpool status $pool | grep "checkpoint:") |
| while [ "" != "$status" ]; do |
| sleep 5 |
| status=$(zpool status $pool | grep "checkpoint:") |
| done |
| } |
| |
| function test_wait_discard_finish |
| { |
| wait_discard_finish $TESTPOOL |
| } |
| |
| function nested_wait_discard_finish |
| { |
| wait_discard_finish $NESTEDPOOL |
| } |
| |
| # |
| # Creating the setup for the second group of tests mentioned in |
| # block comment of this file can take some time as we are doing |
| # random writes to raise capacity and fragmentation before taking |
| # the checkpoint. Thus we create this setup once and save the |
| # disks of the nested pool in a temporary directory where we can |
| # reuse it for each test that requires that setup. |
| # |
| SAVEDPOOLDIR="$TEST_BASE_DIR/ckpoint_saved_pool" |
| |
| function test_group_premake_nested_pools |
| { |
| setup_nested_pools |
| |
| # |
| # Populate and fragment the pool. |
| # |
| fragment_before_checkpoint |
| |
| # |
| # Export and save the pool for other tests. |
| # |
| log_must zpool export $NESTEDPOOL |
| log_must mkdir $SAVEDPOOLDIR |
| log_must cp $FILEDISKS $SAVEDPOOLDIR |
| |
| # |
| # Reimport pool to be destroyed by |
| # cleanup_nested_pools function |
| # |
| log_must zpool import -d $FILEDISKDIR $NESTEDPOOL |
| } |
| |
| function test_group_destroy_saved_pool |
| { |
| log_must rm -rf $SAVEDPOOLDIR |
| } |
| |
| # |
| # Recreate nested pool setup from saved pool. |
| # |
| function setup_nested_pool_state |
| { |
| setup_test_pool |
| |
| log_must zfs create $DISKFS |
| log_must cp $SAVEDPOOLDIR/* $FILEDISKDIR |
| |
| log_must zpool import -d $FILEDISKDIR $NESTEDPOOL |
| } |