From 4291ed51300c0c7c368929fc845d8bb8f0b9f40a Mon Sep 17 00:00:00 2001 From: Sebastian Brzezinka Date: Fri, 2 Sep 2022 15:19:38 +0200 Subject: [PATCH] sma: support bdev-based QoS for vhost_blk devices test/sma verifies that bdev-based QoS settings are correctly applied on vhost devices. Signed-off-by: Sebastian Brzezinka Change-Id: I1b66275dcf457295e6ae58814f1d08ed319fb52a Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/14338 Reviewed-by: Konrad Sztyber Reviewed-by: Tomasz Zawadzki Tested-by: SPDK CI Jenkins --- python/spdk/sma/device/vhost_blk.py | 32 +++++++ test/sma/vhost_blk.sh | 131 ++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/python/spdk/sma/device/vhost_blk.py b/python/spdk/sma/device/vhost_blk.py index d2b4e592a..deed4c3c9 100644 --- a/python/spdk/sma/device/vhost_blk.py +++ b/python/spdk/sma/device/vhost_blk.py @@ -1,9 +1,11 @@ import logging import os +import uuid from socket import AddressFamily import grpc from spdk.rpc.client import JSONRPCException +from spdk.sma import qos from ..common import format_volume_id, volume_id_to_nguid from ..proto import sma_pb2, virtio_blk_pb2 @@ -188,3 +190,33 @@ class VhostBlkDeviceManager(DeviceManager): if not self._delete_controller(client, ctrlr): raise DeviceException(grpc.StatusCode.INTERNAL, 'Failed to delete vhost device') + + def set_qos(self, request): + ctrlr = request.device_handle[len(f'{self._prefix}:'):] + volume = format_volume_id(request.volume_id) + try: + with self._client() as client: + nctrlr = self._find_controller(client, ctrlr) + if nctrlr is None: + raise DeviceException(grpc.StatusCode.INVALID_ARGUMENT, + 'No device associated with device_handle could be found') + nbdev = nctrlr['backend_specific']['block']['bdev'] + if len(request.volume_id) == 0: + id = self._find_bdev(client, nbdev)['uuid'] + request.volume_id = uuid.UUID(id).bytes + elif volume is not None: + if not self._bdev_cmp(client, nbdev, volume): + raise DeviceException(grpc.StatusCode.INVALID_ARGUMENT, + 'Specified volume is not attached to the device') + else: + raise DeviceException(grpc.StatusCode.INVALID_ARGUMENT, + 'Invalid volume uuid') + qos.set_volume_bdev_qos(client, request) + except qos.QosException as ex: + raise DeviceException(ex.code, ex.message) + except JSONRPCException: + raise DeviceException(grpc.StatusCode.INTERNAL, + 'Failed to set QoS') + + def get_qos_capabilities(self, request): + return qos.get_bdev_qos_capabilities() diff --git a/test/sma/vhost_blk.sh b/test/sma/vhost_blk.sh index 4680abad9..3012fc7fa 100755 --- a/test/sma/vhost_blk.sh +++ b/test/sma/vhost_blk.sh @@ -195,5 +195,136 @@ crypto_bdev=$(rpc_cmd bdev_get_bdevs | jq -r '.[] | select(.product_name == "cry delete_device $devid0 [[ $(rpc_cmd bdev_get_bdevs | jq -r '.[] | select(.product_name == "crypto")' | jq -r length) -eq 0 ]] +# Test qos +device_vhost=2 +device=$(create_device 0 $uuid | jq -r '.handle') + +# First check the capabilities +diff <(get_qos_caps $device_vhost | jq --sort-keys) <( + jq --sort-keys <<- CAPS + { + "max_volume_caps": { + "rw_iops": true, + "rd_bandwidth": true, + "wr_bandwidth": true, + "rw_bandwidth": true + } + } + CAPS +) + +"$rootdir/scripts/sma-client.py" <<- EOF + { + "method": "SetQos", + "params": { + "device_handle": "$device", + "volume_id": "$(uuid2base64 $uuid)", + "maximum": { + "rd_iops": 0, + "wr_iops": 0, + "rw_iops": 3, + "rd_bandwidth": 4, + "wr_bandwidth": 5, + "rw_bandwidth": 6 + } + } + } +EOF + +# Make sure that limits were changed +diff <(rpc_cmd bdev_get_bdevs -b null63 | jq --sort-keys '.[].assigned_rate_limits') <( + jq --sort-keys <<- EOF + { + "rw_ios_per_sec": 3000, + "rw_mbytes_per_sec": 6, + "r_mbytes_per_sec": 4, + "w_mbytes_per_sec": 5 + } + EOF +) + +# Try to set capabilities with empty volume id +"$rootdir/scripts/sma-client.py" <<- EOF + { + "method": "SetQos", + "params": { + "device_handle": "$device", + "volume_id": "", + "maximum": { + "rd_iops": 0, + "wr_iops": 0, + "rw_iops": 4, + "rd_bandwidth": 5, + "wr_bandwidth": 6, + "rw_bandwidth": 7 + } + } + } +EOF + +# Make sure that limits were changed even if volume id is not set +diff <(rpc_cmd bdev_get_bdevs -b null63 | jq --sort-keys '.[].assigned_rate_limits') <( + jq --sort-keys <<- EOF + { + "rw_ios_per_sec": 4000, + "rw_mbytes_per_sec": 7, + "r_mbytes_per_sec": 5, + "w_mbytes_per_sec": 6 + } + EOF +) + +# Try none-existing volume uuid +NOT "$rootdir/scripts/sma-client.py" <<- EOF + { + "method": "SetQos", + "params": { + "device_handle": "$device", + "volume_id": "$(uuid2base64 $(uuidgen))", + "maximum": { + "rd_iops": 0, + "wr_iops": 0, + "rw_iops": 5, + "rd_bandwidth": 6, + "wr_bandwidth": 7, + "rw_bandwidth": 8 + } + } + } +EOF + +# Try invalid (too short) volume uuid +NOT "$rootdir/scripts/sma-client.py" <<- EOF + { + "method": "SetQos", + "params": { + "device_handle": "$device", + "volume_id": "$(base64 <<<'invalid'))", + "maximum": { + "rd_iops": 0, + "wr_iops": 0, + "rw_iops": 5, + "rd_bandwidth": 6, + "wr_bandwidth": 7, + "rw_bandwidth": 8 + } + } + } +EOF + +# Values remain unchanged +diff <(rpc_cmd bdev_get_bdevs -b null63 | jq --sort-keys '.[].assigned_rate_limits') <( + jq --sort-keys <<- EOF + { + "rw_ios_per_sec": 4000, + "rw_mbytes_per_sec": 7, + "r_mbytes_per_sec": 5, + "w_mbytes_per_sec": 6 + } + EOF +) + +delete_device "$device" + cleanup trap - SIGINT SIGTERM EXIT