From 9c4054be5960820169ffab1f742b796d96225156 Mon Sep 17 00:00:00 2001 From: Christian Loehle <christian.loehle@arm.com> Date: Wed, 6 Dec 2023 09:47:11 +0000 Subject: [PATCH] lisa.wlgen.fio: Introduce Fio as Workload Fio is a highly-configurable I/O tester used by the block layer for testing and as a general benchmark tool for storage. Even for scheduler investigations Fio is interesting as in_iowait-setting tasks are treated differently. To be able to create tests for them we introduce a simplistic lisa frontend. Fio Workloads can now be created like: ftest = Fio(target, filename="/dev/nvme0n1", name="test", rw="randread", runtime=50, bs='4k') and then ran like any other: with ftest: result = fiotestmmc.run() print(result.read_iops) Signed-off-by: Christian Loehle <christian.loehle@arm.com> --- lisa/wlgen/fio.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 lisa/wlgen/fio.py diff --git a/lisa/wlgen/fio.py b/lisa/wlgen/fio.py new file mode 100644 index 000000000..f50345f63 --- /dev/null +++ b/lisa/wlgen/fio.py @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2023, Arm Limited and contributors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +""" +Fio is a widely used I/O benchmarking tool that is highly configurable. +We use it as in_iowait tasks are treated differently than just blocked tasks, +so anything involving that cannot be generated just with rt-app +""" + +import re +from shlex import quote + +from lisa.utils import memoized, kwargs_forwarded_to +from lisa.wlgen.workload import Workload + + +class FioOutput(str): + """ + A wrapper around fio's terse format output to access some results + XXX: Could also be parsed as json, but will we ever need more than iops? + terse output format is guaranteed to be at stable offsets. + """ + + @property + @memoized + def read_iops(self): + """ + Number of read IOPS as reported by fio + """ + return int(self.split(';')[7]) + + +class Fio(Workload): + """ + A fio workload + + :param target: The Target on which to execute this workload + :type target: Target + + :param filename: Filename for fio to operate on (can be block device) + :type filename: str + + :param testname: The test name passed on to fio + :type filename: str + + :param rw: The mode of operation for fio, e.g. randread, read, rw + :type rw: str + + :param bs: The blocksize as a fio-accepted string (e.g. 512B, 4K, 1M) + :type bs: str + + :param runtime: The time the test should be running for (defaults to seconds) + :type runtime: str + + :param ioengine: The ioengine for fio to use (e.g. psync, libaio, io_uring) + :type ioengine: str + + :param iodepth: The number of requests in-flight at once + :type iodepth: int + + :param numjobs: The number of processes to run this as, will be reported together + :type numjobs: int + + :param cpus_allowed: The set of cpus the fio tasks are allowed on + :type cpus_allowed: str + + :param cli_options: Dictionary of cli_options passed to fio command line. Run + ``fio --help`` for available parameters. Character + ``_`` in option names is replaced by ``-``. + :type cli_options: dict(str, object) + + :Variable keyword arguments: Forwarded to :class:`lisa.wlgen.workload.Workload` + """ + + @kwargs_forwarded_to( + Workload.__init__, + ignore=['command'], + ) + def __init__(self, target, *, + filename, + testname='fiotest', + rw='randread', + bs='4k', + runtime=None, + ioengine="psync", + iodepth=1, + numjobs=1, + cpus_allowed=None, + cli_options=None, + **kwargs + ): + + arg_list = ['fio'] + ['--minimal'] # minimal is terse output format + cli_options = cli_options.copy() if cli_options else {} + cli_options['name'] = testname + cli_options['filename'] = filename + + if runtime: + arg_list += ['--time_based'] + cli_options['runtime'] = runtime + + if numjobs > 1: + arg_list += ['--group_reporting'] # We always want just one report + cli_options['numjobs'] = f"{numjobs}" + + cli_options['rw'] = rw + cli_options['bs'] = bs + cli_options['ioengine'] = ioengine + cli_options['iodepth'] = iodepth + + if cpus_allowed: + cli_options['cpus_allowed'] = cpus_allowed + + arg_list = arg_list + [ + quote(f"--{arg}={value}") + for arg, value in cli_options.items() + ] + + command = ' '.join(arg_list) + + super().__init__(target=target, command=command, **kwargs) + + def _run(self): + out = yield self._basic_run() + return FioOutput(out['stdout']) + +# vim :set tabstop=4 shiftwidth=4 textwidth=80 expandtab -- GitLab