dm-thinp helpers and demo test
cover dm-thinp helpers and demo test
Patch one adds common/dmthin to create & manipulate a dm-thin device
Patch two is a POC test which uses that harness; it may not be at all what we want to test, just putting it out there as a demo.
PATCH 1/2 dm-thinp helpers in common/dmthin
Basic dm-thinp helpers to set up, tear down, grow, check, and set no-space behavior for a single thin dm volume built on $SCRATCH_DEV.
---
common/dmthin new file +238 -0
@@ -0,0 +1,238 @@ +#!/bin/bash +# +# Copyright (c) 2015 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# +# common functions for setting up and tearing down a dmthin device + +# SOOO many devices! +# Create the 2 pool devices on lvs so we can build the whole thing +# from a single scratch device + +# Backing data dev +DMTHIN_DATA_NAME="thin-data" +DMTHIN_DATA_DEV="/dev/mapper/$DMTHIN_DATA_NAME" +# Backing metadata dev +DMTHIN_META_NAME="thin-meta" +DMTHIN_META_DEV="/dev/mapper/$DMTHIN_META_NAME" +# Backing pool dev (combination of above) +DMTHIN_POOL_NAME="thin-pool" +DMTHIN_POOL_DEV="/dev/mapper/$DMTHIN_POOL_NAME" +# Thin volume +DMTHIN_VOL_NAME="thin-vol" +DMTHIN_VOL_DEV="/dev/mapper/$DMTHIN_VOL_NAME" + +_dmthin_cleanup() +{ + $UMOUNT_PROG $SCRATCH_MNT > /dev/null 2>&1 + $DMSETUP_PROG remove $DMTHIN_VOL_NAME> /dev/null 2>&1 + $DMSETUP_PROG remove $DMTHIN_POOL_NAME> /dev/null 2>&1 + $DMSETUP_PROG remove $DMTHIN_META_NAME> /dev/null 2>&1 + $DMSETUP_PROG remove $DMTHIN_DATA_NAME> /dev/null 2>&1 +} + +_dmthin_check_fs() +{ + _check_scratch_fs $DMTHIN_VOL_DEV +} + +# Set up a dm-thin device on $SCRATCH_DEV +# +# All arguments are optional, and in this order; defaults follows: +# data_dev_size: half of $SCRATCH_DEV +# virtual_size: 10x $data_dev_size +# cluster_size: 512k +# low_water: 100M +# +# You may specify 0 to 4 of these arguments, but they +# must be in the above order. +_dmthin_init() +{ + local data_dev_size=$1 # Backing pool data size in sectors + local virtual_size=$2 # Virtual size in sectors + local cluster_size=$3 # cluster/alloc size, sectors + local low_water=$4 # low water mark, sectors + + local dm_backing_dev=$SCRATCH_DEV + local blk_dev_size=`blockdev --getsz $dm_backing_dev` + + local pool_id=$RANDOM + + # Default to something small-ish + if [ -z "$data_dev_size" ]; then + data_dev_size=$(($blk_dev_size / 2)) + fi + + # Default to something big-is; 10x backing + if [ -z "$virtual_size" ]; then + virtual_size=$(($data_dev_size * 10)) + fi + + # Default to 512k + if [ -z "$cluster_size" ]; then + cluster_size=1024 # 512k in sectors + fi + + # Default to 100M + if [ -z "$low_water" ]; then + low_water=204800 # 100M, in sectors + fi + + # Need to make linear metadata and data devs. From kernel docs: + # As a guide, we suggest you calculate the number of bytes to use in the + # metadata device as 48 * $data_dev_size / $data_block_size but round it up + # to 2MB (4096 sectors) if the answer is smaller. + # So do that: + + local meta_dev_size=$((48 * $data_dev_size / $cluster_size)) + if [ "$meta_dev_size" -lt "4096" ]; then + meta_dev_size=4096 # 2MB + fi + + # scratch dev gets a metadata vol & data vol, start at this offset + local meta_dev_offset=10240 + + local total_data_dev_size=$(($meta_dev_offset + $meta_dev_size + $data_dev_size)) + if [ "$total_data_dev_size" -gt "$blk_dev_size" ]; then + _notrun "$SCRATCH_DEV too small" + fi + + # Unmount & tear down old stuff + _dmthin_cleanup + + # Metadata device + DMTHIN_META_TABLE="0 $meta_dev_size linear $dm_backing_dev $meta_dev_offset" + $DMSETUP_PROG create $DMTHIN_META_NAME --table "$DMTHIN_META_TABLE" || \ + _fatal "failed to create dm thin meta device" + + # Data device + local data_dev_offset=$((meta_dev_offset + $meta_dev_size)) + DMTHIN_DATA_TABLE="0 $data_dev_size linear $dm_backing_dev $data_dev_offset" + $DMSETUP_PROG create $DMTHIN_DATA_NAME --table "$DMTHIN_DATA_TABLE" || \ + _fatal "failed to create dm thin data device" + + # Zap the pool metadata dev + dd if=/dev/zero of=$DMTHIN_META_DEV bs=4096 count=1 &>/dev/null + + # Thin pool + # "start length thin-pool metadata_dev data_dev data_block_size low_water_mark" + DMTHIN_POOL_TABLE="0 $data_dev_size thin-pool $DMTHIN_META_DEV $DMTHIN_DATA_DEV $cluster_size $low_water" + $DMSETUP_PROG create $DMTHIN_POOL_NAME --table "$DMTHIN_POOL_TABLE" || \ + _fatal "failed to create dm thin pool device" + + # Thin volume + $DMSETUP_PROG message $DMTHIN_POOL_DEV 0 "create_thin $pool_id" || \ + _fatal "failed to message pool device" + + # start length thin pool_dev dev_id [external_origin_dev] + DMTHIN_VOL_TABLE="0 $virtual_size thin $DMTHIN_POOL_DEV $pool_id" + $DMSETUP_PROG create $DMTHIN_VOL_NAME --table "$DMTHIN_VOL_TABLE" || \ + _fatal "failed to create dm thin volume device" + +} + +# for internal use +_dmthin_reload_table() +{ + local dev_name=$1 + local table="$2" + + $DMSETUP_PROG suspend $dev_name || \ + _fail "dmsetup suspend of $dev_name failed" + + $DMSETUP_PROG load $dev_name --table "$table" || \ + _fail "dmsetup failed to reload $dev_name table" + + $DMSETUP_PROG resume $dev_name || \ + _fail "dmsetup resume of $dev_name failed" + +} + +# Grow the dm-thin device by the given amount +# Argument is number of sectors to add, if not specified +# defaults to 1/4 of the $SCRATCH_DEV size +_dmthin_grow() +{ + local add_sectors=$1 # Number of sectors to add + + local dm_backing_dev=$SCRATCH_DEV + local blk_dev_size=`blockdev --getsz $dm_backing_dev` + + # Get current sizes & values + local meta_dev_size=`$DMSETUP_PROG table | grep $DMTHIN_META_NAME | awk '{print $3}'` + local meta_dev_offset=`$DMSETUP_PROG table | grep $DMTHIN_META_NAME | awk '{print $6}'` + local data_dev_size=`$DMSETUP_PROG table | grep $DMTHIN_DATA_NAME | awk '{print $3}'` + local pool_dev_size=`$DMSETUP_PROG table | grep $DMTHIN_POOL_NAME | awk '{print $3}'` + local cluster_size=`$DMSETUP_PROG table | grep $DMTHIN_POOL_NAME | awk '{print $7}'` + local low_water=`$DMSETUP_PROG table | grep $DMTHIN_POOL_NAME | awk '{print $8}'` + + # default to 25% growth + if [ -z "$add_sectors" ]; then + add_sectors=$(($data_dev_size / 4)) + fi + + local data_dev_offset=$(($meta_dev_offset + $meta_dev_size)) + + # Figure new sizes + data_dev_size=$(($data_dev_size + $add_sectors)) + pool_dev_size=$(($pool_dev_size + $add_sectors)) + + # Can we do this? + local total_data_dev_size=$(($meta_dev_offset + $meta_dev_size + $data_dev_size)) + if [ "$total_data_dev_size" -gt "$blk_dev_size" ]; then + _fail "$SCRATCH_DEV too small" + fi + + # Grow the data device + DMTHIN_DATA_TABLE="0 $data_dev_size linear $dm_backing_dev $data_dev_offset" + _dmthin_reload_table $DMTHIN_DATA_NAME "$DMTHIN_DATA_TABLE" + + # Grow the pool + DMTHIN_POOL_TABLE="0 $data_dev_size thin-pool $DMTHIN_META_DEV $DMTHIN_DATA_DEV $cluster_size $low_water" + _dmthin_reload_table $DMTHIN_POOL_NAME "$DMTHIN_POOL_TABLE" +} + +# Queue IOs when full +_dmthin_set_queue() +{ + local data_dev_size=`$DMSETUP_PROG table | grep $DMTHIN_DATA_NAME | awk '{print $3}'` + local cluster_size=`$DMSETUP_PROG table | grep $DMTHIN_POOL_NAME | awk '{print $7}'` + local low_water=`$DMSETUP_PROG table | grep $DMTHIN_POOL_NAME | awk '{print $8}'` + + DMTHIN_POOL_TABLE="0 $data_dev_size thin-pool $DMTHIN_META_DEV $DMTHIN_DATA_DEV $cluster_size $low_water" + _dmthin_reload_table $DMTHIN_POOL_NAME "$DMTHIN_POOL_TABLE" +} + +# Fail IOs when full +_dmthin_set_fail() +{ + local data_dev_size=`$DMSETUP_PROG table | grep $DMTHIN_DATA_NAME | awk '{print $3}'` + local cluster_size=`$DMSETUP_PROG table | grep $DMTHIN_POOL_NAME | awk '{print $7}'` + local low_water=`$DMSETUP_PROG table | grep $DMTHIN_POOL_NAME | awk '{print $8}'` + + DMTHIN_POOL_TABLE="0 $data_dev_size thin-pool $DMTHIN_META_DEV $DMTHIN_DATA_DEV $cluster_size $low_water 1 error_if_no_space" + _dmthin_reload_table $DMTHIN_POOL_NAME "$DMTHIN_POOL_TABLE" +} + +_dmthin_mount_options() +{ + echo `_common_dev_mount_options $*` $DMTHIN_VOL_DEV $SCRATCH_MNT +} + +_dmthin_mount() +{ + _mount -t $FSTYP `_dmthin_mount_options $*` +}
PATCH 2/2 dm-thinp demo test
In reply to Eric SandeenFairly trivial test to use the dm-thin infrastructure.
Right now it exhausts space in queue-on-error mode, adds more space, does a bit more IO, then unmounts & checks the fs.
Not sure if that's valid to test, but it works here and demonstrates the common/dmthin helpers.
---
tests/generic/115 new file +85 -0
@@ -0,0 +1,85 @@ +#! /bin/bash +# FS QA Test No. generic/115 +# +# Test very basic thin device usage, exhaustion, and growth +# +# Copyright (c) 2015 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +seq=`basename $0` +seqres=$RESULT_DIR/$seq +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ + +status=1 # failure is the default! +trap "_cleanup; exit \$status" 0 1 2 3 15 + +BACKING_SIZE=$((500 * 1024 * 1024 / 512)) # 500M +VIRTUAL_SIZE=$((10 * $BACKING_SIZE)) # 5000M +GROW_SIZE=$((100 * 1024 * 1024 / 512)) # 100M + +_cleanup() +{ + _dmthin_cleanup + rm -f $tmp.* +} + +_setup_thin() +{ + _dmthin_init $BACKING_SIZE $VIRTUAL_SIZE + _dmthin_set_queue + _mkfs_dev $DMTHIN_VOL_DEV + _dmthin_mount +} + +_workout() +{ + # Overfill it by a bit + for I in `seq 1 500`; do + $XFS_IO_PROG -f -c "pwrite -W 0 1M" $SCRATCH_MNT/file$I &>/dev/null + done + + sync + + _dmthin_grow $GROW_SIZE + + # Write a little more, but don't fill + for I in `seq 501 510`; do + $XFS_IO_PROG -f -c "pwrite 0 1M" $SCRATCH_MNT/file$I &>/dev/null + done +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/dmthin + +_supported_fs generic +_supported_os Linux +_need_to_be_root +_require_dm_target thin + +_setup_thin +_dmthin_set_queue +_workout +_dmthin_check_fs +_dmthin_cleanup + +echo "=== completed" + +status=0 +exit
tests/generic/115.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 115 +=== completed
tests/generic/group +1 -0
@@ -115,6 +115,7 @@ 112 rw aio auto quick 113 rw aio auto quick 114 rw aio auto quick +115 rw thin auto quick 117 attr auto quick 120 other auto quick 123 perms auto quick
Re: [PATCH 2/2] dm-thinp demo test
Quoted 84 lines · Eric Sandeen
Fairly trivial test to use the dm-thin infrastructure.
Right now it exhausts space in queue-on-error mode, adds more space, does a bit more IO, then unmounts & checks the fs.
Not sure if that's valid to test, but it works here and demonstrates the common/dmthin helpers.
Signed-off-by Eric Sandeen <[email protected]>---
tests/generic/115 new file +64
-0@@ -0,0 +1,85 @@ +#! /bin/bash +# FS QA Test No. generic/115 +# +# Test very basic thin device usage, exhaustion, and growth +# +# Copyright (c) 2015 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +seq=`basename $0` +seqres=$RESULT_DIR/$seq +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ + +status=1 # failure is the default! +trap "_cleanup; exit \$status" 0 1 2 3 15 + +BACKING_SIZE=$((500 * 1024 * 1024 / 512)) # 500M +VIRTUAL_SIZE=$((10 * $BACKING_SIZE)) # 5000M +GROW_SIZE=$((100 * 1024 * 1024 / 512)) # 100M + +_cleanup() +{ + _dmthin_cleanup + rm -f $tmp.* +} + +_setup_thin() +{ + _dmthin_init $BACKING_SIZE $VIRTUAL_SIZE + _dmthin_set_queue + _mkfs_dev $DMTHIN_VOL_DEV + _dmthin_mount +} + +_workout() +{ + # Overfill it by a bit + for I in `seq 1 500`; do + $XFS_IO_PROG -f -c "pwrite -W 0 1M" $SCRATCH_MNT/file$I &>/dev/null + done + + sync + + _dmthin_grow $GROW_SIZE + + # Write a little more, but don't fill + for I in `seq 501 510`; do + $XFS_IO_PROG -f -c "pwrite 0 1M" $SCRATCH_MNT/file$I &>/dev/null + done
tests/generic/115 line 74
Quoted 10 lines
tests/generic/115 new file +10
-0+} + +# get standard environment, filters and checks +. ./common/rc +. ./common/dmthin + +_supported_fs generic +_supported_os Linux +_need_to_be_root +_require_dm_target thin
This doesn't work for me with 4.3-rc4 kernel. I have to "_require_dm_target thin-pool", modprobe dm-thin reports no module found.
tests/generic/115 line 77
Quoted 3 lines
tests/generic/115 new file +3
-0+ +_setup_thin +_dmthin_set_queue
This one or the one in _setup_thin can be removed?
tests/generic/115 line 79
Quoted 2 lines
tests/generic/115 new file +2
-0+_workout +_dmthin_check_fs
btrfs fails the fs check, because btrfs changes lv device name in df output, from /dev/mapper/xxx to /dev/dm-x, so _check_scratch_fs couldn't umount it and btrfsck runs on mounted btrfs.
Adding "$UMOUNT_PROG $SCRATCH_MNT" before _dmthin_check_fs is a workaround, though I think it's a btrfs issue.
Thanks,
Eryu
Quoted 32 lines
tests/generic/115 new file +6
-0+_dmthin_cleanup + +echo "=== completed" + +status=0 +exittests/generic/115.out new file +2
-0@@ -0,0 +1,2 @@ +QA output created by 115 +=== completedtests/generic/group +1
-1@@ -115,6 +115,7 @@ 112 rw aio auto quick 113 rw aio auto quick 114 rw aio auto quick +115 rw thin auto quick 117 attr auto quick 120 other auto quick 123 perms auto quick --To unsubscribe from this list: send the line "unsubscribe fstests" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/2] dm-thinp demo test
Quoted 91 lines · Eryu Guan
Quoted 90 lines · Eric Sandeen
Fairly trivial test to use the dm-thin infrastructure.
Right now it exhausts space in queue-on-error mode, adds more space, does a bit more IO, then unmounts & checks the fs.
Not sure if that's valid to test, but it works here and demonstrates the common/dmthin helpers.
Signed-off-by Eric Sandeen <[email protected]>---
tests/generic/115 new file +70
-0@@ -0,0 +1,85 @@ +#! /bin/bash +# FS QA Test No. generic/115 +# +# Test very basic thin device usage, exhaustion, and growth +# +# Copyright (c) 2015 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +seq=`basename $0` +seqres=$RESULT_DIR/$seq +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ + +status=1 # failure is the default! +trap "_cleanup; exit \$status" 0 1 2 3 15 + +BACKING_SIZE=$((500 * 1024 * 1024 / 512)) # 500M +VIRTUAL_SIZE=$((10 * $BACKING_SIZE)) # 5000M +GROW_SIZE=$((100 * 1024 * 1024 / 512)) # 100M + +_cleanup() +{ + _dmthin_cleanup + rm -f $tmp.* +} + +_setup_thin() +{ + _dmthin_init $BACKING_SIZE $VIRTUAL_SIZE + _dmthin_set_queue + _mkfs_dev $DMTHIN_VOL_DEV + _dmthin_mount +} + +_workout() +{ + # Overfill it by a bit + for I in `seq 1 500`; do + $XFS_IO_PROG -f -c "pwrite -W 0 1M" $SCRATCH_MNT/file$I &>/dev/null + done + + sync + + _dmthin_grow $GROW_SIZE + + # Write a little more, but don't fill + for I in `seq 501 510`; do + $XFS_IO_PROG -f -c "pwrite 0 1M" $SCRATCH_MNT/file$I &>/dev/null + done +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/dmthin +
Quoted 8 lines
tests/generic/115 line 74
Quoted 4 lines
tests/generic/115 new file +4
-0+_supported_fs generic +_supported_os Linux +_need_to_be_root +_require_dm_target thinThis doesn't work for me with 4.3-rc4 kernel. I have to "
_require_dm_targetthin-pool", modprobe dm-thin reports no module found.
whoops, yep, sorry - i already had it loaded, and the modprobe failure isn't fatal. Should be thin-pool - thanks.
Quoted 5 lines
tests/generic/115 line 77
Quoted 3 lines
tests/generic/115 new file +3
-0+ +_setup_thin +_dmthin_set_queueThis one or the one in
_setup_thincan be removed?
Yep, will do.
Quoted 6 lines
tests/generic/115 line 79
Quoted 2 lines
tests/generic/115 new file +2
-0+_workout +_dmthin_check_fsbtrfs fails the fs check, because btrfs changes lv device name in df output, from
/dev/mapper/xxxto/dev/dm-x, so_check_scratch_fscouldn't umount it and btrfsck runs on mounted btrfs.
Huh, weird. I'll admit that I did not test btrfs. Other filesystems worked for you? I'm not sure how btrfs can change df output...
mapper symlinks have been a constant source of pain for fstests. :(
Quoted 2 lines
Adding "$UMOUNT_PROG $SCRATCH_MNT" before
_dmthin_check_fsis a workaround, though I think it's a btrfs issue.
Hm, ok, thanks; easy enough to do that I guess.
-Eric
Re: [PATCH 2/2] dm-thinp demo test
Quoted 6 lines · Eric Sandeen
Quoted 6 lines
Quoted 6 lines
+_workout
Quoted 1 line
Quoted 1 line
+_dmthin_check_fs
btrfs fails the fs check, because btrfs changes lv device name in df output, from
/dev/mapper/xxxto/dev/dm-x, so_check_scratch_fscouldn't umount it and btrfsck runs on mounted btrfs.
Quoted 8 lines
Huh, weird. I'll admit that I did not test btrfs. Other filesystems worked for you? I'm not sure how btrfs can change df output...
mapper symlinks have been a constant source of pain for fstests. :(
Quoted 2 lines
Quoted 2 lines
Adding "$UMOUNT_PROG $SCRATCH_MNT" before
_dmthin_check_fsis a workaround, though I think it's a btrfs issue.Hm, ok, thanks; easy enough to do that I guess.
ALthough, it seems to be working for me here; I do fail because btrfs emits a warning in dmesg:
[215098.204612] BTRFS warning (device dm-6): Skipping commit of aborted transaction. [215098.212074] ------------[ cut here ]------------ [215098.216794] WARNING: CPU: 3 PID: 19408 at fs/btrfs/transaction.c:1708 cleanup_transaction+0x86/0x2c0 [btrfs]()
which leads to:
_check_dmesg: something found in dmesg (see /mnt/test2/git/xfstests/results//generic/115.dmesg)
Ran: generic/115
Failures: generic/115
It's "just" a warning, though, so I'm not really sure this should be failing the test.
-Eric
Re: [PATCH 1/2] dm-thinp helpers in common/dmthin
In reply to Eric SandeenQuoted 5 lines · Eric Sandeen
Basic dm-thinp helpers to set up, tear down, grow, check, and set no-space behavior for a single thin dm volume built on $SCRATCH_DEV.
Signed-off-by Eric Sandeen <[email protected]>
Looks good to me. We're planning to add more thinp tests using this framework, and we can always improve the dmthin framework. For now
Eryu Guan
Reviewed-byRe: [PATCH v2] fstests: dm-thinp demo test
In reply to a message not in the archiveQuoted 8 lines · Xiong Zhou
From: Eric Sandeen <[email protected]>
Fairly trivial test to use the dm-thin infrastructure.
Right now it exhausts space in queue-on-error mode, adds more space, does a bit more IO, then unmounts & checks the fs.
Quoted 5 lines
Not sure if that's valid to test, but it works here and demonstrates the common/dmthin helpers.
Signed-off-by Eric Sandeen <[email protected]>Signed-off-by Xiong Zhou <[email protected]>
Looks good to me. Thanks!