diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8f9b96c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/tuned-2.16.0.tar.gz
diff --git a/.tuned.metadata b/.tuned.metadata
new file mode 100644
index 0000000..2d7f88a
--- /dev/null
+++ b/.tuned.metadata
@@ -0,0 +1 @@
+e20fcfb734f869fb175cb88dc7ef6e5eb3cd5946 SOURCES/tuned-2.16.0.tar.gz
diff --git a/SOURCES/recommend.conf b/SOURCES/recommend.conf
new file mode 100644
index 0000000..9d32962
--- /dev/null
+++ b/SOURCES/recommend.conf
@@ -0,0 +1,61 @@
+# Tuned rules for recommend_profile.
+#
+# Syntax:
+# [PROFILE1]
+# KEYWORD11=RE11
+# KEYWORD21=RE12
+#
+# [PROFILE2]
+# KEYWORD21=RE21
+# KEYWORD22=RE22
+
+# KEYWORD can be:
+# virt            - for RE to match output of virt-what
+# system          - for RE to match content of /etc/system-release-cpe
+# process         - for RE to match running processes. It can have arbitrary
+#                   suffix, all process* lines have to match for the PROFILE
+#                   to match (i.e. the AND operator)
+# /FILE           - for RE to match content of the FILE, e.g.:
+#                   '/etc/passwd=.+'. If file doesn't exist, its RE will not
+#                   match.
+# chassis_type    - for RE to match the chassis type as reported by dmidecode
+# syspurpose_role - for RE to match the system role as reported by syspurpose
+
+# All REs for all KEYWORDs have to match for PROFILE to match (i.e. the AND operator).
+# If 'virt' or 'system' is not specified, it matches for every string.
+# If 'virt' or 'system' is empty, i.e. 'virt=', it matches only empty string (alias for '^$').
+# If several profiles matched, the first match is taken.
+#
+# Limitation:
+# Each profile can be specified only once, because there cannot be
+# multiple sections in the configuration file with the same name
+# (ConfigObj limitation).
+# If there is a need to specify the profile multiple times, unique
+# suffix like ',ANYSTRING' can be used. Everything after the last ','
+# is stripped by the parser, e.g.:
+#
+# [balanced,1]
+# /FILE1=RE1
+#
+# [balanced,2]
+# /FILE2=RE2
+#
+# This will set 'balanced' profile in case there is FILE1 matching RE1 or
+# FILE2 matching RE2 or both.
+
+[atomic-host]
+virt=
+syspurpose_role=.*atomic.*
+
+[atomic-guest]
+virt=.+
+syspurpose_role=.*atomic.*
+
+[virtual-guest]
+virt=.+
+
+[balanced]
+syspurpose_role=(.*(desktop|workstation).*)|^$
+chassis_type=.*(Notebook|Laptop|Portable).*
+
+[throughput-performance]
diff --git a/SOURCES/tuned-2.16.0-configobj-drop.patch b/SOURCES/tuned-2.16.0-configobj-drop.patch
new file mode 100644
index 0000000..b4bccfb
--- /dev/null
+++ b/SOURCES/tuned-2.16.0-configobj-drop.patch
@@ -0,0 +1,829 @@
+From 063277a05b3a174f9265d36032ca097ee5b7cc9c Mon Sep 17 00:00:00 2001
+From: Jan Zerdik <jzerdik@redhat.com>
+Date: Fri, 30 Jul 2021 11:48:59 +0200
+Subject: [PATCH] Removing dependency on python-configobj.
+
+Resolves: rhbz#1936386
+
+Signed-off-by: Jan Zerdik <jzerdik@redhat.com>
+---
+ recommend.conf                         |  2 +-
+ tests/unit/profiles/test_loader.py     |  7 +++
+ tests/unit/profiles/test_locator.py    | 18 ++++++-
+ tests/unit/profiles/test_variables.py  | 32 ++++++++++++
+ tests/unit/utils/test_global_config.py | 13 ++++-
+ tuned-gui.py                           |  5 +-
+ tuned.spec                             |  3 +-
+ tuned/consts.py                        | 21 ++++++++
+ tuned/gtk/gui_plugin_loader.py         | 43 +++++++++-------
+ tuned/gtk/gui_profile_loader.py        | 71 ++++++++++++++++++--------
+ tuned/gtk/gui_profile_saver.py         | 28 ++++++----
+ tuned/profiles/loader.py               | 38 ++++++--------
+ tuned/profiles/locator.py              | 25 ++++++---
+ tuned/profiles/variables.py            | 27 +++++-----
+ tuned/utils/global_config.py           | 55 +++++++++++++++-----
+ tuned/utils/profile_recommender.py     | 19 ++++---
+ 16 files changed, 288 insertions(+), 119 deletions(-)
+ create mode 100644 tests/unit/profiles/test_variables.py
+
+diff --git a/recommend.conf b/recommend.conf
+index f3442ca8..7561696c 100644
+--- a/recommend.conf
++++ b/recommend.conf
+@@ -29,7 +29,7 @@
+ # Limitation:
+ # Each profile can be specified only once, because there cannot be
+ # multiple sections in the configuration file with the same name
+-# (ConfigObj limitation).
++# (ConfigParser limitation).
+ # If there is a need to specify the profile multiple times, unique
+ # suffix like ',ANYSTRING' can be used. Everything after the last ','
+ # is stripped by the parser, e.g.:
+diff --git a/tests/unit/profiles/test_loader.py b/tests/unit/profiles/test_loader.py
+index b6ea76e9..149353d8 100644
+--- a/tests/unit/profiles/test_loader.py
++++ b/tests/unit/profiles/test_loader.py
+@@ -46,6 +46,8 @@ def setUpClass(cls):
+ 			f.write('file_path=${i:PROFILE_DIR}/whatever\n')
+ 			f.write('script=random_name.sh\n')
+ 			f.write('[test_unit]\ntest_option=hello world\n')
++			f.write('devices=/dev/${variable1},/dev/${variable2}\n')
++			f.write('[variables]\nvariable1=net\nvariable2=cpu')
+ 
+ 	def setUp(self):
+ 		locator = profiles.Locator([self._profiles_dir])
+@@ -105,6 +107,11 @@ def test_load_config_data(self):
+ 		self.assertEqual(config['test_unit']['test_option'],\
+ 			'hello world')
+ 
++	def test_variables(self):
++		config = self._loader.load(['dummy4'])
++		self.assertEqual(config.units['test_unit'].devices,\
++			'/dev/net,/dev/cpu')
++
+ 	@classmethod
+ 	def tearDownClass(cls):
+ 		shutil.rmtree(cls._test_dir)
+diff --git a/tests/unit/profiles/test_locator.py b/tests/unit/profiles/test_locator.py
+index cce88daa..bf2906d7 100644
+--- a/tests/unit/profiles/test_locator.py
++++ b/tests/unit/profiles/test_locator.py
+@@ -30,7 +30,10 @@ def _create_profile(cls, load_dir, profile_name):
+ 		conf_name = os.path.join(profile_dir, "tuned.conf")
+ 		os.mkdir(profile_dir)
+ 		with open(conf_name, "w") as conf_file:
+-			pass
++			if profile_name != "custom":
++				conf_file.write("[main]\nsummary=this is " + profile_name + "\n")
++			else:
++				conf_file.write("summary=this is " + profile_name + "\n")
+ 
+ 	def test_init(self):
+ 		Locator([])
+@@ -65,3 +68,16 @@ def test_ignore_nonexistent_dirs(self):
+ 		self.assertEqual(balanced, os.path.join(self._tmp_load_dirs[0], "balanced", "tuned.conf"))
+ 		known = locator.get_known_names()
+ 		self.assertListEqual(known, ["balanced", "powersafe"])
++
++	def test_get_known_names_summary(self):
++		self.assertEqual(("balanced", "this is balanced"), sorted(self.locator.get_known_names_summary())[0])
++
++	def test_get_profile_attrs(self):
++		attrs = self.locator.get_profile_attrs("balanced", ["summary", "wrong_attr"], ["this is default", "this is wrong attr"])
++		self.assertEqual([True, "balanced", "this is balanced", "this is wrong attr"],  attrs)
++
++		attrs = self.locator.get_profile_attrs("custom", ["summary"], ["wrongly writen profile"])
++		self.assertEqual([True, "custom", "wrongly writen profile"], attrs)
++
++		attrs = self.locator.get_profile_attrs("different", ["summary"], ["non existing profile"])
++		self.assertEqual([False, "", "", ""], attrs)
+diff --git a/tests/unit/profiles/test_variables.py b/tests/unit/profiles/test_variables.py
+new file mode 100644
+index 00000000..47fff2c1
+--- /dev/null
++++ b/tests/unit/profiles/test_variables.py
+@@ -0,0 +1,32 @@
++import unittest
++import tempfile
++import shutil
++from tuned.profiles import variables, profile
++
++class VariablesTestCase(unittest.TestCase):
++
++	@classmethod
++	def setUpClass(cls):
++		cls.test_dir = tempfile.mkdtemp()
++
++		with open(cls.test_dir + "/variables", 'w') as f:
++			f.write("variable1=var1\n")
++
++	def test_from_file(self):
++		v = variables.Variables()
++		v.add_from_file(self.test_dir + "/variables")
++		self.assertEqual("This is var1", v.expand("This is ${variable1}"))
++
++	def test_from_unit(self):
++		mock_unit = {
++			"include": self.test_dir + "/variables",
++			"variable2": "var2"
++		}
++		v = variables.Variables()
++		v.add_from_cfg(mock_unit)
++
++		self.assertEqual("This is var1 and this is var2", v.expand("This is ${variable1} and this is ${variable2}"))
++
++	@classmethod
++	def tearDownClass(cls):
++		shutil.rmtree(cls.test_dir)
+diff --git a/tests/unit/utils/test_global_config.py b/tests/unit/utils/test_global_config.py
+index 5b93888c..8981d544 100644
+--- a/tests/unit/utils/test_global_config.py
++++ b/tests/unit/utils/test_global_config.py
+@@ -12,7 +12,8 @@ def setUpClass(cls):
+ 		cls.test_dir = tempfile.mkdtemp()
+ 		with open(cls.test_dir + '/test_config','w') as f:
+ 			f.write('test_option = hello\ntest_bool = 1\ntest_size = 12MB\n'\
+-				+ 'false_bool=0\n')
++				+ 'false_bool=0\n'\
++				+ consts.CFG_LOG_FILE_COUNT + " = " + str(consts.CFG_DEF_LOG_FILE_COUNT) + "1\n")
+ 
+ 		cls._global_config = global_config.GlobalConfig(\
+ 			cls.test_dir + '/test_config')
+@@ -28,10 +29,18 @@ def test_get_size(self):
+ 		self.assertEqual(self._global_config.get_size('test_size'),\
+ 			12*1024*1024)
+ 
+-		self._global_config.set('test_size','bad_value')
++		self._global_config.set('test_size', 'bad_value')
+ 
+ 		self.assertIsNone(self._global_config.get_size('test_size'))
+ 
++	def test_default(self):
++		daemon = self._global_config.get(consts.CFG_DAEMON)
++		self.assertEqual(daemon, consts.CFG_DEF_DAEMON)
++
++		log_file_count = self._global_config.get(consts.CFG_LOG_FILE_COUNT)
++		self.assertIsNotNone(log_file_count)
++		self.assertNotEqual(log_file_count, consts.CFG_DEF_LOG_FILE_COUNT)
++
+ 	@classmethod
+ 	def tearDownClass(cls):
+ 		shutil.rmtree(cls.test_dir)
+diff --git a/tuned-gui.py b/tuned-gui.py
+index a2792792..3953f82f 100755
+--- a/tuned-gui.py
++++ b/tuned-gui.py
+@@ -48,7 +48,7 @@
+ import sys
+ import os
+ import time
+-import configobj
++import collections
+ import subprocess
+ 
+ import tuned.logs
+@@ -508,8 +508,7 @@ def on_click_button_confirm_profile_update(self, data):
+ 
+ 	def data_to_profile_config(self):
+ 		name = self._gobj('entryProfileName').get_text()
+-		config = configobj.ConfigObj(list_values = False,
+-				interpolation = False)
++		config = collections.OrderedDict()
+ 
+ 		activated = self._gobj('comboboxIncludeProfile').get_active()
+ 		model = self._gobj('comboboxIncludeProfile').get_model()
+diff --git a/tuned.spec b/tuned.spec
+index e3a494fd..7afe1935 100644
+--- a/tuned.spec
++++ b/tuned.spec
+@@ -66,9 +66,8 @@ BuildRequires: %{_py}, %{_py}-devel
+ %if %{without python3} && ( ! 0%{?rhel} || 0%{?rhel} >= 8 )
+ BuildRequires: %{_py}-mock
+ %endif
+-BuildRequires: %{_py}-configobj
+ BuildRequires: %{_py}-pyudev
+-Requires: %{_py}-pyudev, %{_py}-configobj
++Requires: %{_py}-pyudev
+ Requires: %{_py}-linux-procfs, %{_py}-perf
+ %if %{without python3}
+ Requires: %{_py}-schedutils
+diff --git a/tuned/consts.py b/tuned/consts.py
+index 58cbf4a3..8eb075ba 100644
+--- a/tuned/consts.py
++++ b/tuned/consts.py
+@@ -16,6 +16,8 @@
+ LOAD_DIRECTORIES = ["/usr/lib/tuned", "/etc/tuned"]
+ PERSISTENT_STORAGE_DIR = "/var/lib/tuned"
+ PLUGIN_MAIN_UNIT_NAME = "main"
++# Magic section header because ConfigParser does not support "headerless" config
++MAGIC_HEADER_NAME = "this_is_some_magic_section_header_because_of_compatibility"
+ RECOMMEND_DIRECTORIES = ["/usr/lib/tuned/recommend.d", "/etc/tuned/recommend.d"]
+ 
+ TMP_FILE_SUFFIX = ".tmp"
+@@ -79,6 +81,10 @@
+ PREFIX_PROFILE_FACTORY = "System"
+ PREFIX_PROFILE_USER = "User"
+ 
++# After adding new option to tuned-main.conf add here its name with CFG_ prefix
++# and eventually default value with CFG_DEF_ prefix (default is None)
++# and function for check with CFG_FUNC_ prefix
++# (see configobj for methods, default is get for string)
+ CFG_DAEMON = "daemon"
+ CFG_DYNAMIC_TUNING = "dynamic_tuning"
+ CFG_SLEEP_INTERVAL = "sleep_interval"
+@@ -87,25 +93,40 @@
+ CFG_REAPPLY_SYSCTL = "reapply_sysctl"
+ CFG_DEFAULT_INSTANCE_PRIORITY = "default_instance_priority"
+ CFG_UDEV_BUFFER_SIZE = "udev_buffer_size"
++CFG_LOG_FILE_COUNT = "log_file_count"
++CFG_LOG_FILE_MAX_SIZE = "log_file_max_size"
+ CFG_UNAME_STRING = "uname_string"
+ CFG_CPUINFO_STRING = "cpuinfo_string"
+ 
+ # no_daemon mode
+ CFG_DEF_DAEMON = True
++CFG_FUNC_DAEMON = "getboolean"
+ # default configuration
+ CFG_DEF_DYNAMIC_TUNING = True
++CFG_FUNC_DYNAMIC_TUNING = "getboolean"
+ # how long to sleep before checking for events (in seconds)
+ CFG_DEF_SLEEP_INTERVAL = 1
++CFG_FUNC_SLEEP_INTERVAL = "getint"
+ # update interval for dynamic tuning (in seconds)
+ CFG_DEF_UPDATE_INTERVAL = 10
++CFG_FUNC_UPDATE_INTERVAL = "getint"
+ # recommend command availability
+ CFG_DEF_RECOMMEND_COMMAND = True
++CFG_FUNC_RECOMMEND_COMMAND = "getboolean"
+ # reapply system sysctl
+ CFG_DEF_REAPPLY_SYSCTL = True
++CFG_FUNC_REAPPLY_SYSCTL = "getboolean"
+ # default instance priority
+ CFG_DEF_DEFAULT_INSTANCE_PRIORITY = 0
++CFG_FUNC_DEFAULT_INSTANCE_PRIORITY = "getint"
+ # default pyudev.Monitor buffer size
+ CFG_DEF_UDEV_BUFFER_SIZE = 1024 * 1024
++# default log file count
++CFG_DEF_LOG_FILE_COUNT = 2
++CFG_FUNC_LOG_FILE_COUNT = "getint"
++# default log file max size
++CFG_DEF_LOG_FILE_MAX_SIZE = 1024 * 1024
++
+ 
+ PATH_CPU_DMA_LATENCY = "/dev/cpu_dma_latency"
+ 
+diff --git a/tuned/gtk/gui_plugin_loader.py b/tuned/gtk/gui_plugin_loader.py
+index d364602d..f943a220 100644
+--- a/tuned/gtk/gui_plugin_loader.py
++++ b/tuned/gtk/gui_plugin_loader.py
+@@ -25,25 +25,23 @@
+ '''
+ 
+ import importlib
+-from validate import Validator
+ 
+ import tuned.consts as consts
+ import tuned.logs
+-
+-import configobj as ConfigObj
++try:
++    from configparser import ConfigParser, Error
++    from io import StringIO
++except ImportError:
++    # python2.7 support, remove RHEL-7 support end
++    from ConfigParser import ConfigParser, Error
++    from StringIO import StringIO
+ from tuned.exceptions import TunedException
++from tuned.utils.global_config import GlobalConfig
+ 
+ from tuned.admin.dbus_controller import DBusController
+ 
+ __all__ = ['GuiPluginLoader']
+ 
+-global_config_spec = ['dynamic_tuning = boolean(default=%s)'
+-                      % consts.CFG_DEF_DYNAMIC_TUNING,
+-                      'sleep_interval = integer(default=%s)'
+-                      % consts.CFG_DEF_SLEEP_INTERVAL,
+-                      'update_interval = integer(default=%s)'
+-                      % consts.CFG_DEF_UPDATE_INTERVAL]
+-
+ 
+ class GuiPluginLoader():
+ 
+@@ -84,19 +82,26 @@ def _load_global_config(self, file_name=consts.GLOBAL_CONFIG_FILE):
+         """
+ 
+         try:
+-            config = ConfigObj.ConfigObj(file_name,
+-                               configspec=global_config_spec,
+-                               raise_errors = True, file_error = True, list_values = False, interpolation = False)
++            config_parser = ConfigParser()
++            config_parser.optionxform = str
++            with open(file_name) as f:
++                config_parser.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
++            config, functions = GlobalConfig.get_global_config_spec()
++            for option in config_parser.options(consts.MAGIC_HEADER_NAME):
++                if option in config:
++                    try:
++                        func = getattr(config_parser, functions[option])
++                        config[option] = func(consts.MAGIC_HEADER_NAME, option)
++                    except Error:
++                        raise TunedException("Global TuneD configuration file '%s' is not valid."
++                                             % file_name)
++                else:
++                    config[option] = config_parser.get(consts.MAGIC_HEADER_NAME, option, raw=True)
+         except IOError as e:
+             raise TunedException("Global TuneD configuration file '%s' not found."
+                                   % file_name)
+-        except ConfigObj.ConfigObjError as e:
++        except Error as e:
+             raise TunedException("Error parsing global TuneD configuration file '%s'."
+                                   % file_name)
+-        vdt = Validator()
+-        if not config.validate(vdt, copy=True):
+-            raise TunedException("Global TuneD configuration file '%s' is not valid."
+-                                  % file_name)
+         return config
+ 
+-
+diff --git a/tuned/gtk/gui_profile_loader.py b/tuned/gtk/gui_profile_loader.py
+index c50dd9ff..dcd16b72 100644
+--- a/tuned/gtk/gui_profile_loader.py
++++ b/tuned/gtk/gui_profile_loader.py
+@@ -25,10 +25,17 @@
+ '''
+ 
+ import os
+-import configobj
++try:
++    from configparser import ConfigParser, Error
++    from io import StringIO
++except ImportError:
++    # python2.7 support, remove RHEL-7 support end
++    from ConfigParser import ConfigParser, Error
++    from StringIO import StringIO
+ import subprocess
+ import json
+ import sys
++import collections
+ 
+ import tuned.profiles.profile as p
+ import tuned.consts
+@@ -59,14 +66,21 @@ def set_raw_profile(self, profile_name, config):
+ 
+         profilePath = self._locate_profile_path(profile_name)
+ 
+-        config_lines = config.split('\n')
+-
+         if profilePath == tuned.consts.LOAD_DIRECTORIES[1]:
+             file_path = profilePath + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
+-
+-            config_obj = configobj.ConfigObj(infile=config_lines,list_values = False, interpolation = False)
+-            config_obj.filename = file_path
+-            config_obj.initial_comment = ('#', 'tuned configuration', '#')
++            config_parser = ConfigParser()
++            config_parser.optionxform = str
++            config_parser.readfp(StringIO(config))
++
++            config_obj = {
++                'main': collections.OrderedDict(),
++                'filename': file_path,
++                'initial_comment': ('#', 'tuned configuration', '#')
++            }
++            for s in config_parser.sections():
++                config_obj['main'][s] = collections.OrderedDict()
++                for o in config_parser.options(s):
++                    config_obj['main'][s][o] = config_parser.get(s, o, raw=True)
+             self._save_profile(config_obj)
+             self._refresh_profiles()
+         else:
+@@ -76,8 +90,15 @@ def set_raw_profile(self, profile_name, config):
+ 
+     def load_profile_config(self, profile_name, path):
+         conf_path = path + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
+-        profile_config = configobj.ConfigObj(conf_path, list_values = False,
+-			interpolation = False)
++        config = ConfigParser()
++        config.optionxform = str
++        profile_config = collections.OrderedDict()
++        with open(conf_path) as f:
++            config.readfp(f)
++        for s in config.sections():
++            profile_config[s] = collections.OrderedDict()
++            for o in config.options(s):
++                profile_config[s][o] = config.get(s, o, raw=True)
+         return profile_config
+ 
+     def _locate_profile_path(self, profile_name):
+@@ -95,11 +116,11 @@ def _load_all_profiles(self):
+                     try:
+                         self.profiles[profile] = p.Profile(profile,
+                                 self.load_profile_config(profile, d))
+-                    except configobj.ParseError:
++                    except Error:
+                         pass
+ 
+ #                         print "can not make \""+ profile +"\" profile without correct config on path: " + d
+-#                     except:
++#                     except:StringIO
+ #                         raise managerException.ManagerException("Can not make profile")
+ #                         print "can not make \""+ profile +"\" profile without correct config with path: " + d
+ 
+@@ -113,20 +134,24 @@ def _refresh_profiles(self):
+ 
+     def save_profile(self, profile):
+         path = tuned.consts.LOAD_DIRECTORIES[1] + '/' + profile.name
+-        config = configobj.ConfigObj(list_values = False, interpolation = False)
+-        config.filename = path + '/' + tuned.consts.PROFILE_FILE
+-        config.initial_comment = ('#', 'tuned configuration', '#')
++        config = {
++            'main': collections.OrderedDict(),
++            'filename': path + '/' + tuned.consts.PROFILE_FILE,
++            'initial_comment': ('#', 'tuned configuration', '#')
++        }
++        config['filename'] = path + '/' + tuned.consts.PROFILE_FILE
++        config['initial_comment'] = ('#', 'tuned configuration', '#')
+ 
+         try:
+-            config['main'] = profile.options
++            config['main']['main'] = profile.options
+         except KeyError:
+-            config['main'] = ''
++            config['main']['main'] = {}
+ 
+             # profile dont have main section
+ 
+             pass
+         for (name, unit) in list(profile.units.items()):
+-            config[name] = unit.options
++            config['main'][name] = unit.options
+ 
+         self._save_profile(config)
+ 
+@@ -148,18 +173,20 @@ def update_profile(
+         if old_profile_name != profile.name:
+             self.remove_profile(old_profile_name, is_admin=is_admin)
+ 
+-        config = configobj.ConfigObj(list_values = False, interpolation = False)
+-        config.filename = path + '/' + tuned.consts.PROFILE_FILE
+-        config.initial_comment = ('#', 'tuned configuration', '#')
++        config = {
++            'main': collections.OrderedDict(),
++            'filename': path + '/' + tuned.consts.PROFILE_FILE,
++            'initial_comment': ('#', 'tuned configuration', '#')
++        }
+         try:
+-            config['main'] = profile.options
++            config['main']['main'] = profile.options
+         except KeyError:
+ 
+             # profile dont have main section
+ 
+             pass
+         for (name, unit) in list(profile.units.items()):
+-            config[name] = unit.options
++            config['main'][name] = unit.options
+ 
+         self._save_profile(config)
+ 
+diff --git a/tuned/gtk/gui_profile_saver.py b/tuned/gtk/gui_profile_saver.py
+index b339cba1..24b0fe3a 100644
+--- a/tuned/gtk/gui_profile_saver.py
++++ b/tuned/gtk/gui_profile_saver.py
+@@ -1,7 +1,11 @@
+ import os
+ import sys
+ import json
+-from configobj import ConfigObj
++try:
++	from configparser import ConfigParser
++except ImportError:
++	# python2.7 support, remove RHEL-7 support end
++	from ConfigParser import ConfigParser
+ 
+ 
+ if __name__ == "__main__":
+@@ -11,13 +15,19 @@
+ 	if not os.path.exists(profile_dict['filename']):
+ 		os.makedirs(os.path.dirname(profile_dict['filename']))
+ 
+-	profile_configobj = ConfigObj()
+-	for section in profile_dict['sections']:
+-		profile_configobj[section] = profile_dict['main'][section]
+-
+-	profile_configobj.filename = os.path.join('/etc','tuned',os.path.dirname(os.path.abspath(profile_dict['filename'])),'tuned.conf')
+-	profile_configobj.initial_comment = profile_dict['initial_comment']
+-
+-	profile_configobj.write()
++	profile_configobj = ConfigParser()
++	profile_configobj.optionxform = str
++	for section, options in profile_dict['main'].items():
++		profile_configobj.add_section(section)
++		for option, value in options.items():
++			profile_configobj.set(section, option, value)
++
++	path = os.path.join('/etc','tuned',os.path.dirname(os.path.abspath(profile_dict['filename'])),'tuned.conf')
++	with open(path, 'w') as f:
++		profile_configobj.write(f)
++	with open(path, 'r+') as f:
++		content = f.read()
++		f.seek(0, 0)
++		f.write("\n".join(profile_dict['initial_comment']) + "\n" + content)
+ 
+ 	sys.exit(0)
+diff --git a/tuned/profiles/loader.py b/tuned/profiles/loader.py
+index 7f132b4f..31037182 100644
+--- a/tuned/profiles/loader.py
++++ b/tuned/profiles/loader.py
+@@ -1,6 +1,10 @@
+ import tuned.profiles.profile
+ import tuned.profiles.variables
+-from configobj import ConfigObj, ConfigObjError
++try:
++	from configparser import ConfigParser, Error
++except ImportError:
++	# python2.7 support, remove RHEL-7 support end
++	from ConfigParser import ConfigParser, Error
+ import tuned.consts as consts
+ import os.path
+ import collections
+@@ -96,30 +100,22 @@ def _expand_profile_dir(self, profile_dir, string):
+ 
+ 	def _load_config_data(self, file_name):
+ 		try:
+-			config_obj = ConfigObj(file_name, raise_errors = True, list_values = False, interpolation = False)
+-		except ConfigObjError as e:
++			config_obj = ConfigParser()
++			config_obj.optionxform=str
++			with open(file_name) as f:
++				config_obj.readfp(f)
++		except Error as e:
+ 			raise InvalidProfileException("Cannot parse '%s'." % file_name, e)
+ 
+ 		config = collections.OrderedDict()
+-		for section in list(config_obj.keys()):
+-			config[section] = collections.OrderedDict()
+-			try:
+-				keys = list(config_obj[section].keys())
+-			except AttributeError:
+-				raise InvalidProfileException("Error parsing section '%s' in file '%s'." % (section, file_name))
+-			for option in keys:
+-				config[section][option] = config_obj[section][option]
+-
+ 		dir_name = os.path.dirname(file_name)
+-		# TODO: Could we do this in the same place as the expansion of other functions?
+-		for section in config:
+-			for option in config[section]:
++		for section in list(config_obj.sections()):
++			config[section] = collections.OrderedDict()
++			for option in config_obj.options(section):
++				config[section][option] = config_obj.get(section, option, raw=True)
+ 				config[section][option] = self._expand_profile_dir(dir_name, config[section][option])
+-
+-		# TODO: HACK, this needs to be solved in a better way (better config parser)
+-		for unit_name in config:
+-			if "script" in config[unit_name] and config[unit_name].get("script", None) is not None:
+-				script_path = os.path.join(dir_name, config[unit_name]["script"])
+-				config[unit_name]["script"] = [os.path.normpath(script_path)]
++			if config[section].get("script") is not None:
++				script_path = os.path.join(dir_name, config[section]["script"])
++				config[section]["script"] = [os.path.normpath(script_path)]
+ 
+ 		return config
+diff --git a/tuned/profiles/locator.py b/tuned/profiles/locator.py
+index 3fd46916..994bdfb5 100644
+--- a/tuned/profiles/locator.py
++++ b/tuned/profiles/locator.py
+@@ -1,6 +1,12 @@
+ import os
+ import tuned.consts as consts
+-from configobj import ConfigObj, ConfigObjError
++try:
++	from configparser import ConfigParser, Error
++	from io import StringIO
++except ImportError:
++	# python2.7 support, remove RHEL-7 support end
++	from ConfigParser import ConfigParser, Error
++	from StringIO import StringIO
+ 
+ class Locator(object):
+ 	"""
+@@ -48,8 +54,12 @@ def parse_config(self, profile_name):
+ 		if config_file is None:
+ 			return None
+ 		try:
+-			return ConfigObj(config_file, list_values = False, interpolation = False)
+-		except (IOError, OSError, ConfigObjError) as e:
++			config = ConfigParser()
++			config.optionxform = str
++			with open(config_file) as f:
++				config.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
++			return config
++		except (IOError, OSError, Error) as e:
+ 			return None
+ 
+ 	# Get profile attributes (e.g. summary, description), attrs is list of requested attributes,
+@@ -75,17 +85,16 @@ def get_profile_attrs(self, profile_name, attrs, defvals = None):
+ 		config = self.parse_config(profile_name)
+ 		if config is None:
+ 			return [False, "", "", ""]
+-		if consts.PLUGIN_MAIN_UNIT_NAME in config:
+-			d = config[consts.PLUGIN_MAIN_UNIT_NAME]
+-		else:
+-			d = dict()
++		main_unit_in_config = consts.PLUGIN_MAIN_UNIT_NAME in config.sections()
+ 		vals = [True, profile_name]
+ 		for (attr, defval) in zip(attrs, defvals):
+ 			if attr == "" or attr is None:
+ 				vals[0] = False
+ 				vals = vals + [""]
++			elif main_unit_in_config and attr in config.options(consts.PLUGIN_MAIN_UNIT_NAME):
++				vals = vals + [config.get(consts.PLUGIN_MAIN_UNIT_NAME, attr, raw=True)]
+ 			else:
+-				vals = vals + [d.get(attr, defval)]
++				vals = vals + [defval]
+ 		return vals
+ 
+ 	def list_profiles(self):
+diff --git a/tuned/profiles/variables.py b/tuned/profiles/variables.py
+index 2e101661..a9e27aea 100644
+--- a/tuned/profiles/variables.py
++++ b/tuned/profiles/variables.py
+@@ -4,7 +4,13 @@
+ from .functions import functions as functions
+ import tuned.consts as consts
+ from tuned.utils.commands import commands
+-from configobj import ConfigObj, ConfigObjError
++try:
++	from configparser import ConfigParser, Error
++	from io import StringIO
++except ImportError:
++	# python2.7 support, remove RHEL-7 support end
++	from ConfigParser import ConfigParser, Error
++	from StringIO import StringIO
+ 
+ log = tuned.logs.get()
+ 
+@@ -40,24 +46,21 @@ def add_variable(self, variable, value):
+ 		self._lookup_re[r'(?<!\\)\${' + re.escape(s) + r'}'] = v
+ 		self._lookup_env[self._add_env_prefix(s, consts.ENV_PREFIX)] = v
+ 
+-	def add_dict(self, d):
+-		for item in d:
+-			self.add_variable(item, d[item])
+-
+ 	def add_from_file(self, filename):
+ 		if not os.path.exists(filename):
+ 			log.error("unable to find variables_file: '%s'" % filename)
+ 			return
+ 		try:
+-			config = ConfigObj(filename, raise_errors = True, file_error = True, list_values = False, interpolation = False)
+-		except ConfigObjError:
++			config = ConfigParser()
++			config.optionxform = str
++			with open(filename) as f:
++				config.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
++		except Error:
+ 			log.error("error parsing variables_file: '%s'" % filename)
+ 			return
+-		for item in config:
+-			if isinstance(config[item], dict):
+-				self.add_dict(config[item])
+-			else:
+-				self.add_variable(item, config[item])
++		for s in config.sections():
++			for o in config.options(s):
++				self.add_variable(o, config.get(s, o, raw=True))
+ 
+ 	def add_from_cfg(self, cfg):
+ 		for item in cfg:
+diff --git a/tuned/utils/global_config.py b/tuned/utils/global_config.py
+index 039dc9a4..f342700f 100644
+--- a/tuned/utils/global_config.py
++++ b/tuned/utils/global_config.py
+@@ -1,6 +1,11 @@
+ import tuned.logs
+-from configobj import ConfigObj, ConfigObjError
+-from validate import Validator
++try:
++	from configparser import ConfigParser, Error
++	from io import StringIO
++except ImportError:
++	# python2.7 support, remove RHEL-7 support end
++	from ConfigParser import ConfigParser, Error
++	from StringIO import StringIO
+ from tuned.exceptions import TunedException
+ import tuned.consts as consts
+ from tuned.utils.commands import commands
+@@ -11,31 +16,55 @@
+ 
+ class GlobalConfig():
+ 
+-	global_config_spec = ["dynamic_tuning = boolean(default=%s)" % consts.CFG_DEF_DYNAMIC_TUNING,
+-		"sleep_interval = integer(default=%s)" % consts.CFG_DEF_SLEEP_INTERVAL,
+-		"update_interval = integer(default=%s)" % consts.CFG_DEF_UPDATE_INTERVAL,
+-		"recommend_command = boolean(default=%s)" % consts.CFG_DEF_RECOMMEND_COMMAND]
+-
+ 	def __init__(self,config_file = consts.GLOBAL_CONFIG_FILE):
+ 		self._cfg = {}
+ 		self.load_config(file_name=config_file)
+ 		self._cmd = commands()
+ 
++	@staticmethod
++	def get_global_config_spec():
++		"""
++		Easy validation mimicking configobj
++		Returns two dicts, firts with default values (default None)
++		global_default[consts.CFG_SOMETHING] = consts.CFG_DEF_SOMETHING or None
++		second with configobj function for value type (default "get" for string, others eg getboolean, getint)
++		global_function[consts.CFG_SOMETHING] = consts.CFG_FUNC_SOMETHING or get
++		}
++		"""
++		options = [opt for opt in dir(consts)
++				   if opt.startswith("CFG_") and
++				   not opt.startswith("CFG_FUNC_") and
++				   not opt.startswith("CFG_DEF_")]
++		global_default = dict((getattr(consts, opt), getattr(consts, "CFG_DEF_" + opt[4:], None)) for opt in options)
++		global_function = dict((getattr(consts, opt), getattr(consts, "CFG_FUNC_" + opt[4:], "get")) for opt in options)
++		return global_default, global_function
++
+ 	def load_config(self, file_name = consts.GLOBAL_CONFIG_FILE):
+ 		"""
+ 		Loads global configuration file.
+ 		"""
+ 		log.debug("reading and parsing global configuration file '%s'" % file_name)
+ 		try:
+-			self._cfg = ConfigObj(file_name, configspec = self.global_config_spec, raise_errors = True, \
+-				file_error = True, list_values = False, interpolation = False)
++			config_parser = ConfigParser()
++			config_parser.optionxform = str
++			with open(file_name) as f:
++				config_parser.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
++			self._cfg, _global_config_func = self.get_global_config_spec()
++			for option in config_parser.options(consts.MAGIC_HEADER_NAME):
++				if option in self._cfg:
++					try:
++						func = getattr(config_parser, _global_config_func[option])
++						self._cfg[option] = func(consts.MAGIC_HEADER_NAME, option)
++					except Error:
++						raise TunedException("Global TuneD configuration file '%s' is not valid."
++											 % file_name)
++				else:
++					log.info("Unknown option '%s' in global config file '%s'." % (option, file_name))
++					self._cfg[option] = config_parser.get(consts.MAGIC_HEADER_NAME, option, raw=True)
+ 		except IOError as e:
+ 			raise TunedException("Global TuneD configuration file '%s' not found." % file_name)
+-		except ConfigObjError as e:
++		except Error as e:
+ 			raise TunedException("Error parsing global TuneD configuration file '%s'." % file_name)
+-		vdt = Validator()
+-		if (not self._cfg.validate(vdt, copy=True)):
+-			raise TunedException("Global TuneD configuration file '%s' is not valid." % file_name)
+ 
+ 	def get(self, key, default = None):
+ 		return self._cfg.get(key, default)
+diff --git a/tuned/utils/profile_recommender.py b/tuned/utils/profile_recommender.py
+index 580465bb..7300277b 100644
+--- a/tuned/utils/profile_recommender.py
++++ b/tuned/utils/profile_recommender.py
+@@ -3,7 +3,11 @@
+ import errno
+ import procfs
+ import subprocess
+-from configobj import ConfigObj, ConfigObjError
++try:
++	from configparser import ConfigParser, Error
++except ImportError:
++	# python2.7 support, remove RHEL-7 support end
++	from ConfigParser import ConfigParser, Error
+ 
+ try:
+ 	import syspurpose.files
+@@ -59,11 +63,14 @@ def process_config(self, fname, has_root=True):
+ 		try:
+ 			if not os.path.isfile(fname):
+ 				return None
+-			config = ConfigObj(fname, list_values = False, interpolation = False)
+-			for section in list(config.keys()):
++			config = ConfigParser()
++			config.optionxform = str
++			with open(fname) as f:
++				config.readfp(f)
++			for section in config.sections():
+ 				match = True
+-				for option in list(config[section].keys()):
+-					value = config[section][option]
++				for option in config.options(section):
++					value = config.get(section, option, raw=True)
+ 					if value == "":
+ 						value = r"^$"
+ 					if option == "virt":
+@@ -117,7 +124,7 @@ def process_config(self, fname, has_root=True):
+ 					r = re.compile(r",[^,]*$")
+ 					matching_profile = r.sub("", section)
+ 					break
+-		except (IOError, OSError, ConfigObjError) as e:
++		except (IOError, OSError, Error) as e:
+ 			log.error("error processing '%s', %s" % (fname, e))
+ 		return matching_profile
+ 
diff --git a/SOURCES/tuned-2.16.0-scheduler-cgroups-exclude.patch b/SOURCES/tuned-2.16.0-scheduler-cgroups-exclude.patch
new file mode 100644
index 0000000..3f7e597
--- /dev/null
+++ b/SOURCES/tuned-2.16.0-scheduler-cgroups-exclude.patch
@@ -0,0 +1,121 @@
+From 438ff4f899f5eb4bc2ea679fdd2d3611f8e0d8ea Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= <jskarvad@redhat.com>
+Date: Thu, 15 Jul 2021 20:48:54 +0200
+Subject: [PATCH] scheduler: new option cgroup_ps_blacklist
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This option allows skipping processes belonging to the blacklisted
+cgroups. It matches the regular expression against items from the
+/proc/PID/cgroups. Items/lines from the /proc/PID/cgroups are separated
+by commas ','. Each item consists of the:
+hierarchy-ID:controller-list:cgroup-path
+
+Example of the content on which the regular expression is run:
+10:hugetlb:/,9:perf_event:/,8:blkio:/
+
+For cgroups v2 the hierarchy-ID is 0 and the controller-list is ''.
+For details see man cgroups.7. The only difference from the man
+cgroups.7 is that it uses commas for separation of the items instead
+of the new lines. The commas are added by the python-linux-procfs
+(it's the behavior of the python-linux-procfs-0.6.3).
+
+Multiple regular expressions can be separated by the semicolon ';'.
+
+Examples:
+[scheduler]
+isolated_cores=1
+cgroup_ps_blacklist=:/daemons\b
+
+It will move all processes away from the core 1 except processes which
+belongs to the cgroup '/daemons'. The '\b' is regular expression
+metacharacter that matches word boundary (i.e. it matches only
+'/daemons', not e.g. '/daemonset' or '/group/daemons'). In this example
+we do not care about the hierarchy-ID and the controller-list.
+
+[scheduler]
+isolated_cores=1
+cgroup_ps_blacklist=\b8:blkio:/,|$
+
+In this example it skips processes belonging to the cgroup '/',
+with hierarchy-ID 8 and controller-list blkio. The ',|$' is needed
+because the '\b' matches word boundary and the non-alphanumeric
+character '/' is not taken as a word, thus the '\b' will not match there.
+
+[scheduler]
+isolated_cores=1
+cgroup_ps_blacklist=:/daemons\b;:/test\b
+
+In this example two regular expressions are used which tries to match
+'/daemons' and '/test' cgroup-path. If either matches (i.e. the OR operator),
+the process is skipped (i.e. not moved away from the core 1).
+
+Resolves: rhbz#1980715
+
+Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
+---
+ tuned/plugins/plugin_scheduler.py | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/tuned/plugins/plugin_scheduler.py b/tuned/plugins/plugin_scheduler.py
+index e2f7ca2..8e77417 100644
+--- a/tuned/plugins/plugin_scheduler.py
++++ b/tuned/plugins/plugin_scheduler.py
+@@ -156,6 +156,7 @@ class SchedulerPlugin(base.Plugin):
+ 		# default is to whitelist all and blacklist none
+ 		self._ps_whitelist = ".*"
+ 		self._ps_blacklist = ""
++		self._cgroup_ps_blacklist_re = ""
+ 		self._cpus = perf.cpu_map()
+ 		self._scheduler_storage_key = self._storage_key(
+ 				command_name = "scheduler")
+@@ -251,6 +252,7 @@ class SchedulerPlugin(base.Plugin):
+ 			"cgroup_mount_point_init": False,
+ 			"cgroup_groups_init": True,
+ 			"cgroup_for_isolated_cores": None,
++			"cgroup_ps_blacklist": None,
+ 			"ps_whitelist": None,
+ 			"ps_blacklist": None,
+ 			"default_irq_smp_affinity": "calc",
+@@ -811,6 +813,14 @@ class SchedulerPlugin(base.Plugin):
+ 							elif event.type == perf.RECORD_EXIT:
+ 								self._remove_pid(instance, int(event.tid))
+ 
++	@command_custom("cgroup_ps_blacklist", per_device = False)
++	def _cgroup_ps_blacklist(self, enabling, value, verify, ignore_missing):
++		# currently unsupported
++		if verify:
++			return None
++		if enabling and value is not None:
++			self._cgroup_ps_blacklist_re = "|".join(["(%s)" % v for v in re.split(r"(?<!\\);", str(value))])
++
+ 	@command_custom("ps_whitelist", per_device = False)
+ 	def _ps_whitelist(self, enabling, value, verify, ignore_missing):
+ 		# currently unsupported
+@@ -886,6 +896,9 @@ class SchedulerPlugin(base.Plugin):
+ 		if self._ps_blacklist != "":
+ 			psl = [v for v in psl if re.search(self._ps_blacklist,
+ 					self._get_stat_comm(v)) is None]
++		if self._cgroup_ps_blacklist_re != "":
++			psl = [v for v in psl if re.search(self._cgroup_ps_blacklist_re,
++					self._get_stat_cgroup(v)) is None]
+ 		psd = dict([(v.pid, v) for v in psl])
+ 		for pid in psd:
+ 			try:
+@@ -911,6 +924,12 @@ class SchedulerPlugin(base.Plugin):
+ 						psd[pid]["threads"].values(),
+ 						affinity, True)
+ 
++	def _get_stat_cgroup(self, o):
++		try:
++			return o["cgroups"]
++		except (OSError, IOError, KeyError):
++			return ""
++
+ 	def _get_stat_comm(self, o):
+ 		try:
+ 			return o["stat"]["comm"]
+-- 
+2.31.1
+
diff --git a/SPECS/tuned.spec b/SPECS/tuned.spec
new file mode 100644
index 0000000..1370db9
--- /dev/null
+++ b/SPECS/tuned.spec
@@ -0,0 +1,1372 @@
+%if 0%{?fedora}
+%if 0%{?fedora} > 27
+%bcond_without python3
+%else
+%bcond_with python3
+%endif
+%else
+%if 0%{?rhel} && 0%{?rhel} < 8
+%bcond_with python3
+%else
+%bcond_without python3
+%endif
+%endif
+
+%if %{with python3}
+%global _py python3
+%global make_python_arg PYTHON=%{__python3}
+%else
+%{!?python2_sitelib:%global python2_sitelib %{python_sitelib}}
+%if 0%{?rhel} && 0%{?rhel} < 8
+%global make_python_arg PYTHON=%{__python}
+%global _py python
+%else
+%global make_python_arg PYTHON=%{__python2}
+%global _py python2
+%endif
+%endif
+
+#%%global prerelease rc
+#%%global prereleasenum 1
+
+%global prerel1 %{?prerelease:.%{prerelease}%{prereleasenum}}
+%global prerel2 %{?prerelease:-%{prerelease}.%{prereleasenum}}
+
+Summary: A dynamic adaptive system tuning daemon
+Name: tuned
+Version: 2.16.0
+Release: 3%{?prerel1}%{?dist}
+License: GPLv2+
+Source0: https://github.com/redhat-performance/%{name}/archive/v%{version}%{?prerel2}/%{name}-%{version}%{?prerel2}.tar.gz
+# RHEL-9 specific recommend.conf:
+Source1: recommend.conf
+URL: http://www.tuned-project.org/
+BuildArch: noarch
+BuildRequires: systemd, desktop-file-utils
+%if 0%{?rhel}
+BuildRequires: asciidoc
+%else
+BuildRequires: asciidoctor
+%endif
+Requires(post): systemd, virt-what
+Requires(preun): systemd
+Requires(postun): systemd
+BuildRequires: make
+BuildRequires: %{_py}, %{_py}-devel
+# BuildRequires for 'make test'
+# python-mock is needed for python-2.7, but it's not available on RHEL-7
+%if %{without python3} && ( ! 0%{?rhel} || 0%{?rhel} >= 8 )
+BuildRequires: %{_py}-mock
+%endif
+BuildRequires: %{_py}-pyudev
+Requires: %{_py}-pyudev
+Requires: %{_py}-linux-procfs, %{_py}-perf
+%if %{without python3}
+Requires: %{_py}-schedutils
+%endif
+# requires for packages with inconsistent python2/3 names
+%if %{with python3}
+# BuildRequires for 'make test'
+BuildRequires: python3-dbus, python3-gobject-base
+Requires: python3-dbus, python3-gobject-base
+%if 0%{?fedora} > 22 || 0%{?rhel} > 7
+Recommends: dmidecode
+%endif
+%else
+# BuildRequires for 'make test'
+BuildRequires: dbus-python, pygobject3-base
+Requires: dbus-python, pygobject3-base
+%endif
+Requires: virt-what, ethtool, gawk
+Requires: util-linux, dbus, polkit
+%if 0%{?fedora} > 22 || 0%{?rhel} > 7
+Recommends: dmidecode
+Recommends: hdparm
+Recommends: kernel-tools
+Recommends: kmod
+%endif
+# syspurpose
+%if 0%{?rhel} > 8
+Requires: subscription-manager
+%else
+%if 0%{?rhel} > 7
+Requires: python3-syspurpose
+%endif
+%endif
+# rhbz#1980715
+Patch0: tuned-2.16.0-scheduler-cgroups-exclude.patch
+# rhbz#1936386
+Patch1: tuned-2.16.0-configobj-drop.patch
+
+%description
+The tuned package contains a daemon that tunes system settings dynamically.
+It does so by monitoring the usage of several system components periodically.
+Based on that information components will then be put into lower or higher
+power saving modes to adapt to the current usage. Currently only ethernet
+network and ATA harddisk devices are implemented.
+
+%if 0%{?rhel} <= 7 && 0%{!?fedora:1}
+# RHEL <= 7
+%global docdir %{_docdir}/%{name}-%{version}
+%else
+# RHEL > 7 || fedora
+%global docdir %{_docdir}/%{name}
+%endif
+
+%package gtk
+Summary: GTK GUI for tuned
+Requires: %{name} = %{version}-%{release}
+Requires: powertop, polkit
+# requires for packages with inconsistent python2/3 names
+%if %{with python3}
+Requires: python3-gobject-base
+%else
+Requires: pygobject3-base
+%endif
+
+%description gtk
+GTK GUI that can control tuned and provides simple profile editor.
+
+%package utils
+Requires: %{name} = %{version}-%{release}
+Requires: powertop
+Summary: Various tuned utilities
+
+%description utils
+This package contains utilities that can help you to fine tune and
+debug your system and manage tuned profiles.
+
+%package utils-systemtap
+Summary: Disk and net statistic monitoring systemtap scripts
+Requires: %{name} = %{version}-%{release}
+Requires: systemtap
+
+%description utils-systemtap
+This package contains several systemtap scripts to allow detailed
+manual monitoring of the system. Instead of the typical IO/sec it collects
+minimal, maximal and average time between operations to be able to
+identify applications that behave power inefficient (many small operations
+instead of fewer large ones).
+
+%package profiles-sap
+Summary: Additional tuned profile(s) targeted to SAP NetWeaver loads
+Requires: %{name} = %{version}
+
+%description profiles-sap
+Additional tuned profile(s) targeted to SAP NetWeaver loads.
+
+%package profiles-mssql
+Summary: Additional tuned profile(s) for MS SQL Server
+Requires: %{name} = %{version}
+
+%description profiles-mssql
+Additional tuned profile(s) for MS SQL Server.
+
+%package profiles-oracle
+Summary: Additional tuned profile(s) targeted to Oracle loads
+Requires: %{name} = %{version}
+
+%description profiles-oracle
+Additional tuned profile(s) targeted to Oracle loads.
+
+%package profiles-sap-hana
+Summary: Additional tuned profile(s) targeted to SAP HANA loads
+Requires: %{name} = %{version}
+
+%description profiles-sap-hana
+Additional tuned profile(s) targeted to SAP HANA loads.
+
+%package profiles-atomic
+Summary: Additional tuned profile(s) targeted to Atomic
+Requires: %{name} = %{version}
+
+%description profiles-atomic
+Additional tuned profile(s) targeted to Atomic host and guest.
+
+%package profiles-realtime
+Summary: Additional tuned profile(s) targeted to realtime
+Requires: %{name} = %{version}
+Requires: tuna
+
+%description profiles-realtime
+Additional tuned profile(s) targeted to realtime.
+
+%package profiles-nfv-guest
+Summary: Additional tuned profile(s) targeted to Network Function Virtualization (NFV) guest
+Requires: %{name} = %{version}
+Requires: %{name}-profiles-realtime = %{version}
+Requires: tuna
+
+%description profiles-nfv-guest
+Additional tuned profile(s) targeted to Network Function Virtualization (NFV) guest.
+
+%package profiles-nfv-host
+Summary: Additional tuned profile(s) targeted to Network Function Virtualization (NFV) host
+Requires: %{name} = %{version}
+Requires: %{name}-profiles-realtime = %{version}
+Requires: tuna
+Requires: nmap-ncat
+
+%description profiles-nfv-host
+Additional tuned profile(s) targeted to Network Function Virtualization (NFV) host.
+
+# this is kept for backward compatibility, it should be dropped for RHEL-8
+%package profiles-nfv
+Summary: Additional tuned profile(s) targeted to Network Function Virtualization (NFV)
+Requires: %{name} = %{version}
+Requires: %{name}-profiles-nfv-guest = %{version}
+Requires: %{name}-profiles-nfv-host = %{version}
+
+%description profiles-nfv
+Additional tuned profile(s) targeted to Network Function Virtualization (NFV).
+
+%package profiles-cpu-partitioning
+Summary: Additional tuned profile(s) optimized for CPU partitioning
+Requires: %{name} = %{version}
+
+%description profiles-cpu-partitioning
+Additional tuned profile(s) optimized for CPU partitioning.
+
+%package profiles-spectrumscale
+Summary: Additional tuned profile(s) optimized for IBM Spectrum Scale
+Requires: %{name} = %{version}
+
+%description profiles-spectrumscale
+Additional tuned profile(s) optimized for IBM Spectrum Scale.
+
+%package profiles-compat
+Summary: Additional tuned profiles mainly for backward compatibility with tuned 1.0
+Requires: %{name} = %{version}
+
+%description profiles-compat
+Additional tuned profiles mainly for backward compatibility with tuned 1.0.
+It can be also used to fine tune your system for specific scenarios.
+
+%package profiles-postgresql
+Summary: Additional tuned profile(s) targeted to PostgreSQL server loads
+Requires: %{name} = %{version}
+
+%description profiles-postgresql
+Additional tuned profile(s) targeted to PostgreSQL server loads.
+
+%prep
+%autosetup -p1 -n %{name}-%{version}%{?prerel2}
+
+# Replace the upstream recommend.conf with a RHEL-9-specific one
+rm -f recommend.conf
+cp -p %{SOURCE1} recommend.conf
+
+%build
+# Docs cannot be generated on RHEL now due to missing asciidoctor dependency
+# asciidoc doesn't seem to be compatible
+%if ! 0%{?rhel}
+make html %{make_python_arg}
+%endif
+
+%install
+make install DESTDIR=%{buildroot} DOCDIR=%{docdir} %{make_python_arg}
+%if 0%{?rhel}
+sed -i 's/\(dynamic_tuning[ \t]*=[ \t]*\).*/\10/' %{buildroot}%{_sysconfdir}/tuned/tuned-main.conf
+%endif
+
+%if ! 0%{?rhel}
+# manual
+make install-html DESTDIR=%{buildroot} DOCDIR=%{docdir}
+%endif
+
+# conditional support for grub2, grub2 is not available on all architectures
+# and tuned is noarch package, thus the following hack is needed
+mkdir -p %{buildroot}%{_datadir}/tuned/grub2
+mv %{buildroot}%{_sysconfdir}/grub.d/00_tuned %{buildroot}%{_datadir}/tuned/grub2/00_tuned
+rmdir %{buildroot}%{_sysconfdir}/grub.d
+
+# ghost for persistent storage
+mkdir -p %{buildroot}%{_var}/lib/tuned
+
+# ghost for NFV
+mkdir -p %{buildroot}%{_sysconfdir}/modprobe.d
+touch %{buildroot}%{_sysconfdir}/modprobe.d/kvm.rt.tuned.conf
+
+# validate desktop file
+desktop-file-validate %{buildroot}%{_datadir}/applications/tuned-gui.desktop
+
+# Run tests on RHEL > 7 or non RHEL
+# We cannot run tests on RHEL-7 because there is no python-mock package and
+# python-2.7 doesn't have mock built-in
+%if 0%{?rhel} > 7 || ! 0%{?rhel}
+%check
+make test %{make_python_arg}
+%endif
+
+%post
+%systemd_post tuned.service
+
+# convert active_profile from full path to name (if needed)
+sed -i 's|.*/\([^/]\+\)/[^\.]\+\.conf|\1|' /etc/tuned/active_profile
+
+# convert GRUB_CMDLINE_LINUX to GRUB_CMDLINE_LINUX_DEFAULT
+if [ -r "%{_sysconfdir}/default/grub" ]; then
+  sed -i 's/GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX \\$tuned_params"/GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT \\$tuned_params"/' \
+    %{_sysconfdir}/default/grub
+fi
+
+
+%preun
+%systemd_preun tuned.service
+if [ "$1" == 0 ]; then
+# clear persistent storage
+  rm -f %{_var}/lib/tuned/*
+# clear temporal storage
+  rm -f /run/tuned/*
+fi
+
+
+%postun
+%systemd_postun_with_restart tuned.service
+
+# conditional support for grub2, grub2 is not available on all architectures
+# and tuned is noarch package, thus the following hack is needed
+if [ "$1" == 0 ]; then
+  rm -f %{_sysconfdir}/grub.d/00_tuned || :
+# unpatch /etc/default/grub
+  if [ -r "%{_sysconfdir}/default/grub" ]; then
+    sed -i '/GRUB_CMDLINE_LINUX_DEFAULT="${GRUB_CMDLINE_LINUX_DEFAULT:+$GRUB_CMDLINE_LINUX_DEFAULT }\\$tuned_params"/d' %{_sysconfdir}/default/grub
+  fi
+
+# cleanup for Boot loader specification (BLS)
+
+# clear grubenv variables
+  grub2-editenv - unset tuned_params tuned_initrd &>/dev/null || :
+# unpatch BLS entries
+  MACHINE_ID=`cat /etc/machine-id 2>/dev/null`
+  if [ "$MACHINE_ID" ]
+  then
+    for f in /boot/loader/entries/$MACHINE_ID-*.conf
+    do
+      # Skip non-files and rescue entries
+      if [ ! -f "$f" -o "${f: -12}" == "-rescue.conf" ]
+      then
+        continue
+      fi
+      # Skip boom managed entries
+      if [[ "$f" =~ \w*-[0-9a-f]{7,}-.*-.*.conf ]]
+      then
+        continue
+      fi
+      sed -i '/^\s*options\s\+.*\$tuned_params/ s/\s\+\$tuned_params\b//g' "$f" &>/dev/null || :
+      sed -i '/^\s*initrd\s\+.*\$tuned_initrd/ s/\s\+\$tuned_initrd\b//g' "$f" &>/dev/null || :
+    done
+  fi
+fi
+
+
+%triggerun -- tuned < 2.0-0
+# remove ktune from old tuned, now part of tuned
+/usr/sbin/service ktune stop &>/dev/null || :
+/usr/sbin/chkconfig --del ktune &>/dev/null || :
+
+
+%posttrans
+# conditional support for grub2, grub2 is not available on all architectures
+# and tuned is noarch package, thus the following hack is needed
+if [ -d %{_sysconfdir}/grub.d ]; then
+  cp -a %{_datadir}/tuned/grub2/00_tuned %{_sysconfdir}/grub.d/00_tuned
+  selinuxenabled &>/dev/null && \
+    restorecon %{_sysconfdir}/grub.d/00_tuned &>/dev/null || :
+fi
+
+
+%files
+%exclude %{docdir}/README.utils
+%exclude %{docdir}/README.scomes
+%exclude %{docdir}/README.NFV
+%doc %{docdir}
+%{_datadir}/bash-completion/completions/tuned-adm
+%if %{with python3}
+%exclude %{python3_sitelib}/tuned/gtk
+%{python3_sitelib}/tuned
+%else
+%exclude %{python2_sitelib}/tuned/gtk
+%{python2_sitelib}/tuned
+%endif
+%{_sbindir}/tuned
+%{_sbindir}/tuned-adm
+%exclude %{_sysconfdir}/tuned/realtime-variables.conf
+%exclude %{_sysconfdir}/tuned/realtime-virtual-guest-variables.conf
+%exclude %{_sysconfdir}/tuned/realtime-virtual-host-variables.conf
+%exclude %{_sysconfdir}/tuned/cpu-partitioning-variables.conf
+%exclude %{_prefix}/lib/tuned/default
+%exclude %{_prefix}/lib/tuned/desktop-powersave
+%exclude %{_prefix}/lib/tuned/laptop-ac-powersave
+%exclude %{_prefix}/lib/tuned/server-powersave
+%exclude %{_prefix}/lib/tuned/laptop-battery-powersave
+%exclude %{_prefix}/lib/tuned/enterprise-storage
+%exclude %{_prefix}/lib/tuned/spindown-disk
+%exclude %{_prefix}/lib/tuned/sap-netweaver
+%exclude %{_prefix}/lib/tuned/sap-hana
+%exclude %{_prefix}/lib/tuned/mssql
+%exclude %{_prefix}/lib/tuned/oracle
+%exclude %{_prefix}/lib/tuned/atomic-host
+%exclude %{_prefix}/lib/tuned/atomic-guest
+%exclude %{_prefix}/lib/tuned/realtime
+%exclude %{_prefix}/lib/tuned/realtime-virtual-guest
+%exclude %{_prefix}/lib/tuned/realtime-virtual-host
+%exclude %{_prefix}/lib/tuned/cpu-partitioning
+%exclude %{_prefix}/lib/tuned/spectrumscale-ece
+%exclude %{_prefix}/lib/tuned/postgresql
+%{_prefix}/lib/tuned
+%dir %{_sysconfdir}/tuned
+%dir %{_sysconfdir}/tuned/recommend.d
+%dir %{_libexecdir}/tuned
+%{_libexecdir}/tuned/defirqaffinity*
+%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/active_profile
+%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/profile_mode
+%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/post_loaded_profile
+%config(noreplace) %{_sysconfdir}/tuned/tuned-main.conf
+%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/bootcmdline
+%{_sysconfdir}/dbus-1/system.d/com.redhat.tuned.conf
+%verify(not size mtime md5) %{_sysconfdir}/modprobe.d/tuned.conf
+%{_tmpfilesdir}/tuned.conf
+%{_unitdir}/tuned.service
+%dir %{_localstatedir}/log/tuned
+%dir /run/tuned
+%dir %{_var}/lib/tuned
+%{_mandir}/man5/tuned*
+%{_mandir}/man7/tuned-profiles.7*
+%{_mandir}/man8/tuned*
+%dir %{_datadir}/tuned
+%{_datadir}/tuned/grub2
+%{_datadir}/polkit-1/actions/com.redhat.tuned.policy
+%ghost %{_sysconfdir}/modprobe.d/kvm.rt.tuned.conf
+%{_prefix}/lib/kernel/install.d/92-tuned.install
+
+%files gtk
+%{_sbindir}/tuned-gui
+%if %{with python3}
+%{python3_sitelib}/tuned/gtk
+%else
+%{python2_sitelib}/tuned/gtk
+%endif
+%{_datadir}/tuned/ui
+%{_datadir}/icons/hicolor/scalable/apps/tuned.svg
+%{_datadir}/applications/tuned-gui.desktop
+
+%files utils
+%doc COPYING
+%{_bindir}/powertop2tuned
+%{_libexecdir}/tuned/pmqos-static*
+
+%files utils-systemtap
+%doc doc/README.utils
+%doc doc/README.scomes
+%doc COPYING
+%{_sbindir}/varnetload
+%{_sbindir}/netdevstat
+%{_sbindir}/diskdevstat
+%{_sbindir}/scomes
+%{_mandir}/man8/varnetload.*
+%{_mandir}/man8/netdevstat.*
+%{_mandir}/man8/diskdevstat.*
+%{_mandir}/man8/scomes.*
+
+%files profiles-sap
+%{_prefix}/lib/tuned/sap-netweaver
+%{_mandir}/man7/tuned-profiles-sap.7*
+
+%files profiles-sap-hana
+%{_prefix}/lib/tuned/sap-hana
+%{_mandir}/man7/tuned-profiles-sap-hana.7*
+
+%files profiles-mssql
+%{_prefix}/lib/tuned/mssql
+%{_mandir}/man7/tuned-profiles-mssql.7*
+
+%files profiles-oracle
+%{_prefix}/lib/tuned/oracle
+%{_mandir}/man7/tuned-profiles-oracle.7*
+
+%files profiles-atomic
+%{_prefix}/lib/tuned/atomic-host
+%{_prefix}/lib/tuned/atomic-guest
+%{_mandir}/man7/tuned-profiles-atomic.7*
+
+%files profiles-realtime
+%config(noreplace) %{_sysconfdir}/tuned/realtime-variables.conf
+%{_prefix}/lib/tuned/realtime
+%{_mandir}/man7/tuned-profiles-realtime.7*
+
+%files profiles-nfv-guest
+%config(noreplace) %{_sysconfdir}/tuned/realtime-virtual-guest-variables.conf
+%{_prefix}/lib/tuned/realtime-virtual-guest
+%{_mandir}/man7/tuned-profiles-nfv-guest.7*
+
+%files profiles-nfv-host
+%config(noreplace) %{_sysconfdir}/tuned/realtime-virtual-host-variables.conf
+%{_prefix}/lib/tuned/realtime-virtual-host
+%{_mandir}/man7/tuned-profiles-nfv-host.7*
+
+%files profiles-nfv
+%doc %{docdir}/README.NFV
+
+%files profiles-cpu-partitioning
+%config(noreplace) %{_sysconfdir}/tuned/cpu-partitioning-variables.conf
+%{_prefix}/lib/tuned/cpu-partitioning
+%{_mandir}/man7/tuned-profiles-cpu-partitioning.7*
+
+%files profiles-spectrumscale
+%{_prefix}/lib/tuned/spectrumscale-ece
+%{_mandir}/man7/tuned-profiles-spectrumscale-ece.7*
+
+%files profiles-compat
+%{_prefix}/lib/tuned/default
+%{_prefix}/lib/tuned/desktop-powersave
+%{_prefix}/lib/tuned/laptop-ac-powersave
+%{_prefix}/lib/tuned/server-powersave
+%{_prefix}/lib/tuned/laptop-battery-powersave
+%{_prefix}/lib/tuned/enterprise-storage
+%{_prefix}/lib/tuned/spindown-disk
+%{_mandir}/man7/tuned-profiles-compat.7*
+
+%files profiles-postgresql
+%{_prefix}/lib/tuned/postgresql
+%{_mandir}/man7/tuned-profiles-postgresql.7*
+
+%changelog
+* Wed Aug 18 2021 Jaroslav Škarvada <jskarvad@redhat.com> - 2.16.0-3
+- scheduler: allow exclude of processes from the specific cgroup(s)
+  Resolves: rhbz#1980715
+- Switched to the configparser from the configobj
+  Resolves: rhbz#1936386
+
+* Tue Aug 10 2021 Mohan Boddu <mboddu@redhat.com> - 2.16.0-2
+- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
+  Related: rhbz#1991688
+
+* Wed Jul 21 2021 Jaroslav Škarvada <jskarvad@redhat.com> - 2.16.0-1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1944643
+  - realtime: disabled kvm.nx_huge_page kernel module option in
+    realtime-virtual-host profile
+    resolves: rhbz#1976825
+  - realtime: explicitly set 'irqaffinity=~<isolated_cpu_mask>' in kernel
+    command line
+    resolves: rhbz#1974820
+  - scheduler: added abstraction for the sched_* and numa_* variables which
+    were previously accessible through the sysctl
+    resolves: rhbz#1952687
+  - recommend: fixed wrong profile on ppc64le bare metal servers
+    resolves: rhbz#1959889
+
+* Thu May 27 2021 Jaroslav Škarvada <jskarvad@redhat.com> - 2.15.0-6
+- Dropped python-schedutils
+  Resolves: rhbz#1964680
+
+* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 2.15.0-5
+- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
+
+* Mon Apr 12 2021 Jaroslav Škarvada <jskarvad@redhat.com> - 2.15.0-4
+- updated syspurpose requirement for RHEL-9
+  resolves: rhbz#1948764
+
+* Mon Feb 08 2021 Jan Zerdik <jzerdik@redhat.com> - 2.15.0-3
+- used RHEL recommend.conf
+  resolves: rhbz#1921016
+
+* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2.15.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Thu Dec 17 2020 Jaroslav Škarvada <jskarvad@redhat.com> - 2.15.0-1
+- new release
+  - rebased tuned to latest upstream
+    related: rhbz#1874052
+
+* Tue Dec  1 2020 Jaroslav Škarvada <jskarvad@redhat.com> - 2.15.0-0.1.rc1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1874052
+  - added plugin service for linux services control
+    resolves: rhbz#1869991
+  - scheduler: added default_irq_smp_affinity option
+    resolves: rhbz#1896348
+  - bootloader: skip boom managed BLS snippets
+    resolves: rhbz#1901532
+  - scheduler: added perf_process_fork option to enable processing of fork
+    resolves: rhbz#1894610
+  - scheduler: added perf_mmap_pages option to set perf buffer size
+    resolves: rhbz#1890219
+  - bootloader: fixed cmdline duplication with BLS and grub2-mkconfig
+    resolves: rhbz#1777874
+
+* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.14.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Fri Jul  3 2020 Jaroslav Škarvada <jskarvad@redhat.com> - 2.14.0-2
+- scheduler: fixed isolated_cores to work with cgroups
+  related: rhbz#1784648
+- throughput-performance: fix performance regression on AMD platforms
+  related: rhbz#1746957
+
+* Mon Jun 22 2020 Jaroslav Škarvada <jskarvad@redhat.com> - 2.14.0-1
+- new release
+  - rebased tuned to latest upstream
+    related: rhbz#1792264
+
+* Mon Jun  8 2020 Jaroslav Škarvada <jskarvad@redhat.com> - 2.14.0-0.1.rc1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1792264
+  - oracle: turned off NUMA balancing
+    resolves: rhbz#1782233
+  - man: documented the possibility to apply multiple profiles
+    resolves: rhbz#1794337
+  - cpu-partitioning: disabled kernel.timer_migration
+    resolves: rhbz#1797629
+  - profiles: new profile optimize-serial-console
+    resolves: rhbz#1840689
+  - added support for a post-loaded profile
+    resolves: rhbz#1798183
+  - plugins: new irqbalance plugin
+    resolves: rhbz#1784645
+  - throughput-performance: added architecture specific tuning for Marvell ThunderX
+    resolves: rhbz#1746961
+  - throughput-performance: added architecture specific tuning for AMD
+    resolves: rhbz#1746957
+  - scheduler: added support for cgroups
+    resolves: rhbz#1784648
+
+* Tue May 26 2020 Miro Hrončok <mhroncok@redhat.com> - 2.13.0-4
+- Rebuilt for Python 3.9
+
+* Mon Apr 06 2020 Miro Hrončok <mhroncok@redhat.com> - 2.13.0-3
+- Build without unittest2
+
+* Fri Jan 31 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.13.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Wed Dec 11 2019 Jaroslav Škarvada <jskarvad@redhat.com> - 2.13.0-1
+- new release
+  - rebased tuned to latest upstream
+    related: rhbz#1738250
+  - sap-hana: updated tuning
+    resolves: rhbz#1779821
+  - latency-performance: updated tuning
+    resolves: rhbz#1779759
+  - added sst profile
+    resolves: rhbz#1743879
+
+* Sun Dec  1 2019 Jaroslav Škarvada <jskarvad@redhat.com> - 2.13.0-0.1.rc1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1738250
+  - cpu: fixed checking if EPB is supported
+    resolves: rhbz#1690929
+  - scheduler: fixed IRQ SMP affinity verification to respect ignore_missing
+    resolves: rhbz#1729936
+  - realtime: enabled ktimer_lockless_check
+    resolves: rhbz#1734096
+  - plugins: support cpuinfo_regex and uname_regex matching
+    resolves: rhbz#1748965
+  - sysctl: made reapply_sysctl ignore configs from /usr
+    resolves: rhbz#1759597
+  - added support for multiple include directives
+    resolves: rhbz#1760390
+  - realtime: added nowatchdog kernel command line option
+    resolves: rhbz#1767614
+
+* Thu Oct 03 2019 Miro Hrončok <mhroncok@redhat.com> - 2.12.0-4
+- Rebuilt for Python 3.8.0rc1 (#1748018)
+
+* Mon Aug 19 2019 Miro Hrončok <mhroncok@redhat.com> - 2.12.0-3
+- Rebuilt for Python 3.8
+
+* Sat Jul 27 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.12.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Thu Jun 27 2019 Jaroslav Škarvada <jskarvad@redhat.com> - 2.12.0-1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1685585
+
+* Wed Jun 12 2019 Jaroslav Škarvada <jskarvad@redhat.com> - 2.12.0-0.1.rc1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1685585
+  - sap-netweaver: changed values of kernel.shmall and kernel.shmmax to RHEL-8 defaults
+    resolves: rhbz#1708418
+  - sap-netweaver: changed value of kernel.sem to RHEL-8 default
+    resolves: rhbz#1701394
+  - sap-hana-vmware: dropped profile
+    resolves: rhbz#1715541
+  - s2kb function: fixed to be compatible with python3
+    resolves: rhbz#1684122
+  - do fallback to the powersave governor (balanced and powersave profiles)
+    resolves: rhbz#1679205
+  - added support for negation of CPU list
+    resolves: rhbz#1676588
+  - switched from sysctl tool to own implementation
+    resolves: rhbz#1666678
+  - realtime-virtual-host: added tsc-deadline=on to qemu cmdline
+    resolves: rhbz#1554458
+  - fixed handling of devices that have been removed and re-attached
+    resolves: rhbz#1677730
+
+* Thu Mar 21 2019 Jaroslav Škarvada <jskarvad@redhat.com> - 2.11.0-1
+- new release
+  - rebased tuned to latest upstream
+    related: rhbz#1643654
+  - used dmidecode only on x86 architectures
+    resolves: rhbz#1688371
+  - recommend: fixed to work without tuned daemon running
+    resolves: rhbz#1687397
+  - powertop2tuned: added support for wakeup tuning (powertop-2.10)
+    resolves: rhbz#1690354
+
+* Sun Mar 10 2019 Jaroslav Škarvada <jskarvad@redhat.com> - 2.11.0-0.1.rc1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1643654
+  - use online CPUs for cpusets calculations instead of present CPUs
+    resolves: rhbz#1613478
+  - realtime-virtual-guest: run script.sh
+    related: rhbz#1616043
+  - make python-dmidecode a weak dependency
+    resolves: rhbz#1565598
+  - make virtual-host identical to latency-performance
+    resolves: rhbz#1588932
+  - added support for Boot loader specification (BLS)
+    resolves: rhbz#1576435
+  - scheduler: keep polling file objects alive long enough
+    resolves: rhbz#1659140
+  - mssql: updated tuning
+    resolves: rhbz#1660178
+  - s2kb: fixed to be compatible with python3
+    resolves: rhbz#1684122
+  - profiles: fallback to the 'powersave' scaling governor
+    resolves: rhbz#1679205
+  - disable KSM only once, re-enable it only on full rollback
+    resolves: rhbz#1622239
+  - functions: reworked setup_kvm_mod_low_latency to count with kernel changes
+    resolves: rhbz#1649408
+  - updated virtual-host profile
+    resolves: rhbz#1569375
+  - added log message for unsupported parameters in plugin_net
+    resolves: rhbz#1533852
+  - added range feature for cpu exclusion
+    resolves: rhbz#1533908
+  - make a copy of devices when verifying tuning
+    resolves: rhbz#1592743
+  - fixed disk plugin/plugout problem
+    resolves: rhbz#1595156
+  - fixed unit configuration reading
+    resolves: rhbz#1613379
+  - reload profile configuration on SIGHUP
+    resolves: rhbz#1631744
+  - use built-in functionality to apply system sysctl
+    resolves: rhbz#1663412
+
+* Sun Feb 03 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.10.0-7
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Fri Jan 25 2019 Jaroslav Škarvada <jskarvad@redhat.com> - 2.10.0-6
+- Fixed disk plugin to correctly match devices with python3
+
+* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.10.0-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Wed Jul 11 2018 Ondřej Lysoněk <olysonek@redhat.com> - 2.10.0-4
+- Fix a traceback in tuned-gui
+
+* Tue Jul 10 2018 Ondřej Lysoněk <olysonek@redhat.com> - 2.10.0-3
+- Used python intepreter path from the rpm macro
+
+* Tue Jul 10 2018 Ondřej Lysoněk <olysonek@redhat.com> - 2.10.0-2
+- tuned-adm: Fix a traceback when run without action specified
+- Fixed compatibility with python-3.7
+
+* Wed Jul  4 2018 Jaroslav Škarvada <jskarvad@redhat.com> - 2.10.0-1
+- new release
+  - rebased tuned to latest upstream
+    related: rhbz#1546598
+  - IRQ affinity handled by scheduler plugin
+    resolves: rhbz#1590937
+
+* Mon Jun 11 2018 Jaroslav Škarvada <jskarvad@redhat.com> - 2.10.0-0.1.rc1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1546598
+  - script: show stderr output in the log
+    resolves: rhbz#1536476
+  - realtime-virtual-host: script.sh: add error checking
+    resolves: rhbz#1461509
+  - man: improved tuned-profiles-cpu-partitioning.7
+    resolves: rhbz#1548148
+  - bootloader: check if grub2_cfg_file_name is None in _remove_grub2_tuning()
+    resolves: rhbz#1571403
+  - plugin_scheduler: whitelist/blacklist processed also for thread names
+    resolves: rhbz#1512295
+  - bootloader: patch all GRUB2 config files
+    resolves: rhbz#1556990
+  - profiles: added mssql profile
+    resolves: rhbz#1442122
+  - tuned-adm: print log excerpt when changing profile
+    resolves: rhbz#1538745
+  - cpu-partitioning: use no_balance_cores instead of no_rebalance_cores
+    resolves: rhbz#1550573
+  - sysctl: support assignment modifiers as other plugins do
+    resolves: rhbz#1564092
+  - oracle: fixed ip_local_port_range parity warning
+    resolves: rhbz#1527219
+  - Fix verifying cpumask on systems with more than 32 cores
+    resolves: rhbz#1528368
+  - oracle: updated the profile to be in sync with KCS 39188
+    resolves: rhbz#1447323
+
+* Fri Mar 23 2018 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-9
+- Used weak deps for tuned-profiles-nfv-host-bin
+
+* Wed Mar 21 2018 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-8
+- Dropped tuned-profiles-nfv-host-bin, now provided by standalone package
+
+* Fri Mar  2 2018 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-7
+- Dropped exlusive arch in tuned-profiles-nfv-host-bin (it seems it
+  blocked all tuned packages on non x86 architectures)
+
+* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.9.0-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Mon Feb 05 2018 Iryna Shcherbina <ishcherb@redhat.com> - 2.9.0-5
+- Update Python 2 dependency declarations to new packaging standards
+  (See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3)
+
+* Wed Jan 31 2018 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-4
+- Fixed perf requirement, explicitly require python2-perf
+
+* Sat Jan 06 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 2.9.0-3
+- Remove obsolete scriptlets
+
+* Mon Nov 13 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-2
+- added tscdeadline_latency.flat benchmark
+  resolves: rhbz#1504680
+
+* Sun Oct 29 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-1
+- new release
+  - rebased tuned to latest upstream
+    related: rhbz#1467576
+
+* Fri Oct 20 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-0.2.rc2
+- new release
+  - rebased tuned to latest upstream
+    related: rhbz#1467576
+  - fixed expansion of the variables in the 'devices' section
+    related: rhbz#1490399
+  - cpu-partitioning: add no_rebalance_cores= option
+    resolves: rhbz#1497182
+
+* Thu Oct 12 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.9.0-0.1.rc1
+- new release
+  - rebased tuned to latest upstream
+    resolves: rhbz#1467576
+  - added recommend.d functionality
+    resolves: rhbz#1459146
+  - recommend: added support for matching of processes
+    resolves: rhbz#1461838
+  - plugin_video: added support for the 'dpm' power method
+    resolves: rhbz#1417659
+  - list available profiles on 'tuned-adm profile'
+    resolves: rhbz#988433
+  - cpu-partitioning: used tuned instead of tuna for cores isolation
+    resolves: rhbz#1442229
+  - inventory: added workaround for pyudev < 0.18
+    resolves: rhbz#1251240
+  - realtime: used skew_tick=1 in kernel cmdline
+    resolves: rhbz#1447938
+  - realtime-virtual-guest: re-assigned kernel thread priorities
+    resolves: rhbz#1452357
+  - bootloader: splitted string for removal from cmdline
+    resolves: rhbz#1461279
+  - network-latency: added skew_tick=1 kernel command line parameter
+    resolves: rhbz#1451073
+  - bootloader: accepted only certain values for initrd_remove_dir
+    resolves: rhbz#1455161
+  - increased udev monitor buffer size, made it configurable
+    resolves: rhbz#1442306
+  - bootloader: don't add nonexistent overlay image to grub.cfg
+    resolves: rhbz#1454340
+  - plugin_cpu: don't log error in execute() if EPB is not supported
+    resolves: rhbz#1443182
+  - sap-hana: fixed description of the sap-hana profiles
+    resolves: rhbz#1482005
+  - plugin_systemd: on full_rollback notify about need of initrd regeneration
+    resolves: rhbz#1469258
+  - don't log errors about missing files on verify with ignore_missing set
+    resolves: rhbz#1451435
+  - plugin_scheduler: improved logging
+    resolves: rhbz#1474961
+  - improved checking if we are rebooting or not
+    resolves: rhbz#1475571
+  - started dbus exports after a profile is applied
+    resolves: rhbz#1443142
+  - sap-hana: changed force_latency to 70
+    resolves: rhbz#1501252
+
+* Mon Aug 21 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.8.0-5
+- kernel-tools made weak dependency
+
+* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 2.8.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Mon Jul 24 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.8.0-3
+- fixed traceback in disk plugin if dynamic tuning is enabled
+
+* Fri Apr 28 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.8.0-2
+- qemu-kvm-tools-rhev made weak dependency
+
+* Fri Apr  7 2017 Jaroslav Škarvada <jskarvad@redhat.com> - 2.8.0-1
+- new release
+  - rebase tuned to latest upstream
+    resolves: rhbz#1388454
+  - cpu-partitioning: enabled timer migration
+    resolves: rhbz#1408308
+  - cpu-partitioning: disabled kvmclock sync and ple
+    resolves: rhbz#1395855
+  - spec: muted error if there is no selinux support
+    resolves: rhbz#1404214
+  - units: implemented instance priority
+    resolves: rhbz#1246172
+  - bootloader: added support for initrd overlays
+    resolves: rhbz#1414098
+  - cpu-partitioning: set CPUAffinity early in initrd image
+    resolves: rhbz#1394965
+  - cpu-partitioning: set workqueue affinity early
+    resolves: rhbz#1395899
+  - scsi_host: fixed probing of ALPM, missing ALPM logged as info
+    resolves: rhbz#1416712
+  - added new profile cpu-partitioning
+    resolves: rhbz#1359956
+  - bootloader: improved inheritance
+    resolves: rhbz#1274464
+  - units: mplemented udev-based regexp device matching
+    resolves: rhbz#1251240
+  - units: introduced pre_script, post_script
+    resolves: rhbz#1246176
+  - realtime-virtual-host: accommodate new ktimersoftd thread
+    resolves: rhbz#1332563
+  - defirqaffinity: fixed traceback due to syntax error
+    resolves: rhbz#1369791
+  - variables: support inheritance of variables
+    resolves: rhbz#1433496
+  - scheduler: added support for cores isolation
+    resolves: rhbz#1403309
+  - tuned-profiles-nfv splitted to host/guest and dropped unneeded dependency
+    resolves: rhbz#1413111
+  - desktop: fixed typo in profile summary
+    resolves: rhbz#1421238
+  - with systemd don't do full rollback on shutdown / reboot
+    resolves: rhbz#1421286
+  - builtin functions: added virt_check function and support to include
+    resolves: rhbz#1426654
+  - cpulist_present: explicitly sorted present CPUs
+    resolves: rhbz#1432240
+  - plugin_scheduler: fixed initialization
+    resolves: rhbz#1433496
+  - log errors when applying a profile fails
+    resolves: rhbz#1434360
+  - systemd: added support for older systemd CPUAffinity syntax
+    resolves: rhbz#1441791
+  - scheduler: added workarounds for low level exceptions from
+    python-linux-procfs
+    resolves: rhbz#1441792
+  - bootloader: workaround for adding tuned_initrd to new kernels on restart
+    resolves: rhbz#1441797
+
+* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 2.7.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Mon Oct 17 2016 Jaroslav Škarvada <jskarvad@redhat.com> - 2.7.1-3
+- Fixed traceback when non-existent profile is queried through
+  tuned-adm profile_info
+  Resolves: rhbz#1385145
+
+* Wed Sep 21 2016 Jaroslav Škarvada <jskarvad@redhat.com> - 2.7.1-2
+- Fixed pkexec
+  Resolves: rhbz#1377896
+
+* Tue Aug  2 2016 Jaroslav Škarvada <jskarvad@redhat.com> - 2.7.1-1
+- New release
+  Resolves: rhbz#1362481
+- Dropped tuned-gui-traceback-fix and tuned-adm-list-no-dbus-traceback-fix
+  patches (both upstreamed)
+
+* Thu Jul 21 2016 Jaroslav Škarvada <jskarvad@redhat.com> - 2.7.0-2
+- Fixed tuned-gui traceback (by tuned-gui-traceback-fix patch)
+  Resolves: rhbz#1358846
+- Fixed 'tuned-adm list' traceback if daemon is not running
+  (by tuned-adm-list-no-dbus-traceback-fix patch)
+
+* Tue Jul 19 2016 Jaroslav Škarvada <jskarvad@redhat.com> - 2.7.0-1
+- new-release
+  - gui: fixed save profile
+    resolves: rhbz#1242491
+  - tuned-adm: added --ignore-missing parameter
+    resolves: rhbz#1243807
+  - plugin_vm: added transparent_hugepage alias
+    resolves: rhbz#1249610
+  - plugins: added modules plugin
+    resolves: rhbz#1249618
+  - plugin_cpu: do not show error if cpupower or x86_energy_perf_policy are
+    missing
+    resolves: rhbz#1254417
+  - tuned-adm: fixed restart attempt if tuned is not running
+    resolves: rhbz#1258755
+  - nfv: avoided race condition by using synchronous mode
+    resolves: rhbz#1259039
+  - realtime: added check for isolcpus sanity
+    resolves: rhbz#1264128
+  - pm_qos: fixed exception if PM_QoS is not available
+    resolves: rhbz#1296137
+  - plugin_sysctl: reapply system sysctl after Tuned sysctl are applied
+    resolves: rhbz#1302953
+  - atomic: increase number of inotify watches
+    resolves: rhbz#1322001
+  - realtime-virtual-host/guest: added rcu_nocbs kernel boot parameter
+    resolves: rhbz#1334479
+  - realtime: fixed kernel.sched_rt_runtime_us to be -1
+    resolves: rhbz#1346715
+  - tuned-adm: fixed detection of no_daemon mode
+    resolves: rhbz#1351536
+  - plugin_base: correctly strip assignment modifiers even if not used
+    resolves: rhbz#1353142
+  - plugin_disk: try to workaround embedded '/' in device names
+    related: rhbz#1353142
+  - sap-hana: explicitly setting kernel.numa_balancing = 0 for better performance
+    resolves: rhbz#1355768
+  - switched to polkit authorization
+    resolves: rhbz#1095142
+  - plugins: added scsi_host plugin
+    resolves: rhbz#1246992
+  - spec: fixed conditional support for grub2 to work with selinux
+    resolves: rhbz#1351937
+  - gui: added tuned icon and desktop file
+    resolves: rhbz#1356369
+
+* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.6.0-3
+- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
+
+* Fri Feb 05 2016 Fedora Release Engineering <releng@fedoraproject.org> - 2.6.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Tue Jan  5 2016 Jaroslav Škarvada <jskarvad@redhat.com> - 2.6.0-1
+- new-release
+  - plugin_cpu: do not show error if cpupower or x86_energy_perf_policy are missing
+  - plugin_sysctl: fixed quoting of sysctl values
+    resolves: rhbz#1254538
+  - tuned-adm: added log file location hint to verify command output
+  - libexec: fixed listdir and isdir in defirqaffinity.py
+    resolves: rhbz#1252160
+  - plugin_cpu: save and restore only intel pstate attributes that were changed
+    resolves: rhbz#1252156
+  - functions: fixed sysfs save to work with options
+    resolves: rhbz#1251507
+  - plugins: added scsi_host plugin
+  - tuned-adm: fixed restart attempt if tuned is not running
+  - spec: fixed post scriptlet to work without grub
+    resolves: rhbz#1265654
+  - tuned-profiles-nfv: fix find-lapictscdeadline-optimal.sh for CPUS where ns > 6500
+    resolves: rhbz#1267284
+  - functions: fixed restore_logs_syncing to preserve SELinux context on rsyslog.conf
+    resolves: rhbz#1268901
+  - realtime: set unboud workqueues cpumask
+    resolves: rhbz#1259043
+  - spec: correctly remove tuned footprint from /etc/default/grub
+    resolves: rhbz#1268845
+  - gui: fixed creation of new profile
+    resolves: rhbz#1274609
+  - profiles: removed nohz_full from the realtime profile
+    resolves: rhbz#1274486
+  - profiles: Added nohz_full and nohz=on to realtime guest/host profiles
+    resolves: rhbz#1274445
+  - profiles: fixed lapic_timer_adv_ns cache
+    resolves: rhbz#1259452
+  - plugin_sysctl: pass verification even if the option doesn't exist
+    related: rhbz#1252153
+  - added support for 'summary' and 'description' of profiles,
+    extended D-Bus API for Cockpit
+    related: rhbz#1228356
+
+* Wed Aug 12 2015 Jaroslav Škarvada <jskarvad@redhat.com> - 2.5.1-2
+- packaging fixes for rpm-4.12.90
+- dropped qemu-kvm-tools-rhev requirement (not in Fedora)
+
+* Tue Aug  4 2015 Jaroslav Škarvada <jskarvad@redhat.com> - 2.5.1-1
+- new-release
+  related: rhbz#1155052
+  - plugin_scheduler: work with nohz_full
+    resolves: rhbz#1247184
+  - fixed realtime-virtual-guest/host profiles packaged twice
+    resolves: rhbz#1249028
+  - fixed requirements of realtime and nfv profiles
+  - fixed tuned-gui not starting
+  - various other minor fixes
+
+* Sun Jul  5 2015 Jaroslav Škarvada <jskarvad@redhat.com> - 2.5.0-1
+- new-release
+  resolves: rhbz#1155052
+  - add support for ethtool -C to tuned network plugin
+    resolves: rhbz#1152539
+  - add support for ethtool -K to tuned network plugin
+    resolves: rhbz#1152541
+  - add support for calculation of values for the kernel command line
+    resolves: rhbz#1191595
+  - no error output if there is no hdparm installed
+    resolves: rhbz#1191775
+  - do not run hdparm on hotplug events if there is no hdparm tuning
+    resolves: rhbz#1193682
+  - add oracle tuned profile
+    resolves: rhbz#1196298
+  - fix bash completions for tuned-adm
+    resolves: rhbz#1207668
+  - add glob support to tuned sysfs plugin
+    resolves: rhbz#1212831
+  - add tuned-adm verify subcommand
+    resolves: rhbz#1212836
+  - do not install tuned kernel command line to rescue kernels
+    resolves: rhbz#1223864
+  - add variables support
+    resolves: rhbz#1225124
+  - add built-in support for unit conversion into tuned
+    resolves: rhbz#1225135
+  - fix vm.max_map_count setting in sap-netweaver profile
+    resolves: rhbz#1228562
+  - add tuned profile for RHEL-RT
+    resolves: rhbz#1228801
+  - plugin_scheduler: added support for runtime tuning of processes
+    resolves: rhbz#1148546
+  - add support for changing elevators on xvd* devices (Amazon EC2)
+    resolves: rhbz#1170152
+  - add workaround to be run after systemd-sysctl
+    resolves: rhbz#1189263
+  - do not change settings of transparent hugepages if set in kernel cmdline
+    resolves: rhbz#1189868
+  - add tuned profiles for RHEL-NFV
+    resolves: rhbz#1228803
+  - plugin_bootloader: apply $tuned_params to existing kernels
+    resolves: rhbz#1233004
+
+* Fri Jun 19 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.4.1-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Thu Apr 30 2015 Jaroslav Škarvada <jskarvad@redhat.com> - 2.4.1-5
+- fixed configobj class imports
+  resolves: rhbz#1217327
+
+* Thu Apr  2 2015 Jaroslav Škarvada <jskarvad@redhat.com> - 2.4.1-4
+- fixed bash completion
+  resolves: rhbz#1207668
+
+* Fri Jan  9 2015 Jaroslav Škarvada <jskarvad@redhat.com> - 2.4.1-3
+- fixed KeyError exception in powertop2tuned
+
+* Mon Jan  5 2015 Jaroslav Škarvada <jskarvad@redhat.com> - 2.4.1-2
+- remove 00_tuned grub2 template upon tuned uninstall
+  resolves: rhbz#1178310
+
+* Thu Oct 16 2014 Jaroslav Škarvada <jskarvad@redhat.com> - 2.4.1-1
+- new-release
+  - fixed return code of tuned grub template
+    resolves: rhbz#1151768
+  - plugin_bootloader: fix for multiple parameters on command line
+    related: rhbz#1148711
+  - tuned-adm: fixed traceback on "tuned-adm list"
+    resolves: rhbz#1149162
+  - plugin_bootloader is automatically disabled if grub2 is not found
+    resolves: rhbz#1150047
+  - plugin_disk: set_spindown and set_APM made independent
+    resolves: rhbz#976725
+
+* Wed Oct  1 2014 Jaroslav Škarvada <jskarvad@redhat.com> - 2.4.0-1
+- new-release
+  resolves: rhbz#1093883
+  - fixed traceback if profile cannot be loaded
+    related: rhbz#953128
+  - powertop2tuned: fixed traceback if rewriting file instead of dir
+    resolves: rhbz#963441
+  - throughput-performance: altered dirty ratios for better performance
+    resolves: rhbz#1043533
+  - latency-performance: leaving THP on its default
+    resolves: rhbz#1064510
+  - used throughput-performance profile on server by default
+    resolves: rhbz#1063481
+  - network-latency: added new profile
+    resolves: rhbz#1052418
+  - network-throughput: added new profile
+    resolves: rhbz#1052421
+  - recommend.conf: fixed config file
+    resolves: rhbz#1069123
+  - systemd: added cpupower.service conflict
+    resolves: rhbz#1073392
+  - balanced: used medium_power ALPM policy
+  - added support for >, < assignment modifiers in tuned.conf
+  - handled root block devices
+  - balanced: used conservative CPU governor
+    resolves: rhbz#1124125
+  - plugins: added selinux plugin
+  - plugin_net: added nf_conntrack_hashsize parameter
+  - profiles: added atomic-host profile
+    resolves: rhbz#1091977
+  - profiles: added atomic-guest profile
+    resolves: rhbz#1091979
+  - moved profile autodetection from post install script to tuned daemon
+    resolves: rhbz#1144067
+  - profiles: included sap-hana and sap-hana-vmware profiles
+  - man: structured profiles manual pages according to sub-packages
+  - added missing hdparm dependency
+    resolves: rhbz#1144858
+  - improved error handling of switch_profile
+    resolves: rhbz#1068699
+  - tuned-adm: active: detect whether tuned deamon is running
+    related: rhbz#1068699
+  - removed active_profile from RPM verification
+    resolves: rhbz#1104126
+  - plugin_disk: readahead value can be now specified in sectors
+    resolves: rhbz#1127127
+  - plugins: added bootloader plugin
+    resolves: rhbz#1044111
+  - plugin_disk: added error counter to hdparm calls
+  - plugins: added scheduler plugin
+    resolves: rhbz#1100826
+  - added tuned-gui
+
+* Thu Mar  6 2014 Jaroslav Škarvada <jskarvad@redhat.com> - 2.3.0-3
+- added kernel-tools requirement
+  resolves: rhbz#1072981
+
+* Fri Nov  8 2013 Jaroslav Škarvada <jskarvad@redhat.com> - 2.3.0-2
+- fixed race condition in the start/stop code
+  resolves: rhbz#1028119
+- improved tuned responsiveness
+  resolves: rhbz#1028122
+
+* Wed Nov  6 2013 Jaroslav Škarvada <jskarvad@redhat.com> - 2.3.0-1
+- new-release
+  resolves: rhbz#1020743
+  - audio plugin: fixed audio settings in standard profiles
+    resolves: rhbz#1019805
+  - video plugin: fixed tunings
+  - daemon: fixed crash if preset profile is not available
+    resolves: rhbz#953128
+  - man: various updates and corrections
+  - functions: fixed usb and bluetooth handling
+  - tuned: switched to lightweighted pygobject3-base
+  - daemon: added global config for dynamic_tuning
+    resolves: rhbz#1006427
+  - utils: added pmqos-static script for debug purposes
+    resolves: rhbz#1015676
+  - throughput-performance: various fixes
+    resolves: rhbz#987570
+  - tuned: added global option update_interval
+  - plugin_cpu: added support for x86_energy_perf_policy
+    resolves: rhbz#1015675
+  - dbus: fixed KeyboardInterrupt handling
+  - plugin_cpu: added support for intel_pstate
+    resolves: rhbz#996722
+  - profiles: various fixes
+    resolves: rhbz#922068
+  - profiles: added desktop profile
+    resolves: rhbz#996723
+  - tuned-adm: implemented non DBus fallback control
+  - profiles: added sap profile
+  - tuned: lowered CPU usage due to python bug
+    resolves: rhbz#917587
+
+* Tue Mar 19 2013 Jaroslav Škarvada <jskarvad@redhat.com> - 2.2.2-1
+- new-release:
+  - cpu plugin: fixed cpupower workaround
+  - cpu plugin: fixed crash if cpupower is installed
+
+* Fri Mar  1 2013 Jaroslav Škarvada <jskarvad@redhat.com> - 2.2.1-1
+- new release:
+  - audio plugin: fixed error handling in _get_timeout
+  - removed cpupower dependency, added sysfs fallback
+  - powertop2tuned: fixed parser crash on binary garbage
+    resolves: rhbz#914933
+  - cpu plugin: dropped multicore_powersave as kernel upstream already did
+  - plugins: options manipulated by dynamic tuning are now correctly saved and restored
+  - powertop2tuned: added alias -e for --enable option
+  - powertop2tuned: new option -m, --merge-profile to select profile to merge
+  - prefer transparent_hugepage over redhat_transparent_hugepage
+  - recommend: use recommend.conf not autodetect.conf
+  - tuned.service: switched to dbus type service
+    resolves: rhbz#911445
+  - tuned: new option --pid, -P to write PID file
+  - tuned, tuned-adm: added new option --version, -v to show version
+  - disk plugin: use APM value 254 for cleanup / APM disable instead of 255
+    resolves: rhbz#905195
+  - tuned: new option --log, -l to select log file
+  - powertop2tuned: avoid circular deps in include (one level check only)
+  - powertop2tuned: do not crash if powertop is not installed
+  - net plugin: added support for wake_on_lan static tuning
+    resolves: rhbz#885504
+  - loader: fixed error handling
+  - spec: used systemd-rpm macros
+    resolves: rhbz#850347
+
+* Mon Jan 28 2013 Jan Vcelak <jvcelak@redhat.com> 2.2.0-1
+- new release:
+  - remove nobarrier from virtual-guest (data loss prevention)
+  - devices enumeration via udev, instead of manual retrieval
+  - support for dynamically inserted devices (currently disk plugin)
+  - dropped rfkill plugins (bluetooth and wifi), the code didn't work
+
+* Wed Jan  2 2013 Jaroslav Škarvada <jskarvad@redhat.com> - 2.1.2-1
+- new release:
+  - systemtap {disk,net}devstat: fix typo in usage
+  - switched to configobj parser
+  - latency-performance: disabled THP
+  - fixed fd leaks on subprocesses
+
+* Thu Dec 06 2012 Jan Vcelak <jvcelak@redhat.com> 2.1.1-1
+- fix: powertop2tuned execution
+- fix: ownership of /etc/tuned
+
+* Mon Dec 03 2012 Jan Vcelak <jvcelak@redhat.com> 2.1.0-1
+- new release:
+  - daemon: allow running without selected profile
+  - daemon: fix profile merging, allow only safe characters in profile names
+  - daemon: implement missing methods in DBus interface
+  - daemon: implement profile recommendation
+  - daemon: improve daemonization, PID file handling
+  - daemon: improved device matching in profiles, negation possible
+  - daemon: various internal improvements
+  - executables: check for EUID instead of UID
+  - executables: run python with -Es to increase security
+  - plugins: cpu - fix cpupower execution
+  - plugins: disk - fix option setting
+  - plugins: mounts - new, currently supports only barriers control
+  - plugins: sysctl - fix a bug preventing settings application
+  - powertop2tuned: speedup, fix crashes with non-C locales
+  - powertop2tuned: support for powertop 2.2 output
+  - profiles: progress on replacing scripts with plugins
+  - tuned-adm: bash completion - suggest profiles from all supported locations
+  - tuned-adm: complete switch to D-bus
+  - tuned-adm: full control to users with physical access
+
+* Mon Oct 08 2012 Jaroslav Škarvada <jskarvad@redhat.com> - 2.0.2-1
+- New version
+- Systemtap scripts moved to utils-systemtap subpackage
+
+* Sun Jul 22 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.0.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Tue Jun 12 2012 Jaroslav Škarvada <jskarvad@redhat.com> - 2.0.1-3
+- another powertop-2.0 compatibility fix
+  Resolves: rhbz#830415
+
+* Tue Jun 12 2012 Jan Kaluza <jkaluza@redhat.com> - 2.0.1-2
+- fixed powertop2tuned compatibility with powertop-2.0
+
+* Tue Apr 03 2012 Jaroslav Škarvada <jskarvad@redhat.com> - 2.0.1-1
+- new version
+
+* Fri Mar 30 2012 Jan Vcelak <jvcelak@redhat.com> 2.0-1
+- first stable release