diff --git a/.gitignore b/.gitignore
index 677b0c2..eeeef49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/tuna-0.14.tar.xz
+SOURCES/tuna-0.15.tar.xz
diff --git a/.tuna.metadata b/.tuna.metadata
index 83d1a26..54b2adf 100644
--- a/.tuna.metadata
+++ b/.tuna.metadata
@@ -1 +1 @@
-0957ee39985f0414dfeabd85daf6c7357c009891 SOURCES/tuna-0.14.tar.xz
+67fb3ea8815809203ca5f37941a03bee3dc1422f SOURCES/tuna-0.15.tar.xz
diff --git a/SOURCES/tuna-Use-exception-args-attribute-for-python3.patch b/SOURCES/tuna-Use-exception-args-attribute-for-python3.patch
deleted file mode 100644
index f8898bd..0000000
--- a/SOURCES/tuna-Use-exception-args-attribute-for-python3.patch
+++ /dev/null
@@ -1,243 +0,0 @@
-From 0c70baee8d3af97a130bc745adda85e530bd30b0 Mon Sep 17 00:00:00 2001
-From: John Kacur <jkacur@redhat.com>
-Date: Tue, 4 Dec 2018 01:36:19 +0100
-Subject: [PATCH 2/2] tuna: Use exception args attribute for python3
-
-In python3 exceptions are not interable, so use the args attribute
-
-Signed-off-by: John Kacur <jkacur@redhat.com>
----
- tuna-cmd.py      |  4 ++--
- tuna/gui/util.py |  4 ++--
- tuna/tuna.py     | 46 +++++++++++++++++++++++-----------------------
- 3 files changed, 27 insertions(+), 27 deletions(-)
-
-diff --git a/tuna-cmd.py b/tuna-cmd.py
-index 8068695ae6df..e4182231d80f 100755
---- a/tuna-cmd.py
-+++ b/tuna-cmd.py
-@@ -182,7 +182,7 @@ def ps_show_thread(pid, affect_children, ps,
-         try:
-                 affinity = format_affinity(schedutils.get_affinity(pid))
-         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                if e[0] == errno.ESRCH:
-+                if e.args[0] == errno.ESRCH:
-                         return
-                 raise e
- 
-@@ -264,7 +264,7 @@ def ps_show(ps, affect_children, thread_list, cpu_list,
-                 try:
-                         affinity = schedutils.get_affinity(pid)
-                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                        if e[0] == errno.ESRCH:
-+                        if e.args[0] == errno.ESRCH:
-                                 continue
-                         raise e
-                 if cpu_list and not set(cpu_list).intersection(set(affinity)):
-diff --git a/tuna/gui/util.py b/tuna/gui/util.py
-index 73eceae23ab7..9e30ed92bd4c 100755
---- a/tuna/gui/util.py
-+++ b/tuna/gui/util.py
-@@ -86,7 +86,7 @@ def thread_set_attributes(pid_info, new_policy, new_prio, new_affinity, nr_cpus)
- 	try:
- 		curr_affinity = schedutils.get_affinity(pid)
- 	except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError
--		if e[0] == 3:
-+		if e.args[0] == 3:
- 			return False
- 		raise e
- 
-@@ -109,7 +109,7 @@ def thread_set_attributes(pid_info, new_policy, new_prio, new_affinity, nr_cpus)
- 		try:
- 			curr_affinity = schedutils.get_affinity(pid)
- 		except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError
--			if e[0] == 3:
-+			if e.args[0] == 3:
- 				return False
- 			raise e
- 
-diff --git a/tuna/tuna.py b/tuna/tuna.py
-index 9f9267863871..4b1a77bcfa9f 100755
---- a/tuna/tuna.py
-+++ b/tuna/tuna.py
-@@ -193,7 +193,7 @@ def move_threads_to_cpu(cpus, pid_list, set_affinity_warning = None,
-                         try:
-                                 curr_affinity = schedutils.get_affinity(pid)
-                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                if e[0] == errno.ESRCH:
-+                                if e.args[0] == errno.ESRCH:
-                                         continue
-                                 curr_affinity = None
-                                 raise e
-@@ -202,7 +202,7 @@ def move_threads_to_cpu(cpus, pid_list, set_affinity_warning = None,
-                                         schedutils.set_affinity(pid, new_affinity)
-                                         curr_affinity = schedutils.get_affinity(pid)
-                                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                        if e[0] == errno.ESRCH:
-+                                        if e.args[0] == errno.ESRCH:
-                                                 continue
-                                         curr_affinity == None
-                                         raise e
-@@ -231,7 +231,7 @@ def move_threads_to_cpu(cpus, pid_list, set_affinity_warning = None,
-                                 try:
-                                         curr_affinity = schedutils.get_affinity(tid)
-                                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                        if e[0] == errno.ESRCH:
-+                                        if e.args[0] == errno.ESRCH:
-                                                 continue
-                                         raise e
-                                 if set(curr_affinity) != set(new_affinity):
-@@ -239,7 +239,7 @@ def move_threads_to_cpu(cpus, pid_list, set_affinity_warning = None,
-                                                 schedutils.set_affinity(tid, new_affinity)
-                                                 curr_affinity = schedutils.get_affinity(tid)
-                                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                                if e[0] == errno.ESRCH:
-+                                                if e.args[0] == errno.ESRCH:
-                                                         continue
-                                                 raise e
-                                         if set(curr_affinity) == set(new_affinity):
-@@ -251,10 +251,10 @@ def move_threads_to_cpu(cpus, pid_list, set_affinity_warning = None,
-                                                       (_("could not change %(pid)d affinity to %(new_affinity)s") % \
-                                                        {'pid':pid, 'new_affinity':new_affinity}))
-                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                        if e[0] == errno.ESRCH:
-+                        if e.args[0] == errno.ESRCH:
-                                 # process died
-                                 continue
--                        elif e[0] == errno.EINVAL: # unmovable thread)
-+                        elif e.args[0] == errno.EINVAL: # unmovable thread)
-                                 print("thread %(pid)d cannot be moved as requested" %{'pid':pid}, file=stderr)
-                                 continue
-                         raise e
-@@ -301,7 +301,7 @@ def move_irqs_to_cpu(cpus, irq_list, spread = False):
-                         try:
-                                 schedutils.set_affinity(pid, new_affinity)
-                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                if e[0] == errno.ESRCH:
-+                                if e.args[0] == errno.ESRCH:
-                                         unprocessed.append(i)
-                                         changed -= 1
-                                         continue
-@@ -336,9 +336,9 @@ def isolate_cpus(cpus, nr_cpus):
-                 try:
-                         affinity = schedutils.get_affinity(pid)
-                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                        if e[0] == errno.ESRCH:
-+                        if e.args[0] == errno.ESRCH:
-                                 continue
--                        elif e[0] == errno.EINVAL:
-+                        elif e.args[0] == errno.EINVAL:
-                             print("Function:", fname, ",", e.strerror, file=sys.stderr)
-                             sys.exit(2)
-                         raise e
-@@ -348,9 +348,9 @@ def isolate_cpus(cpus, nr_cpus):
-                         try:
-                                 schedutils.set_affinity(pid, affinity)
-                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                if e[0] == errno.ESRCH:
-+                                if e.args[0] == errno.ESRCH:
-                                         continue
--                                elif e[0] == errno.EINVAL:
-+                                elif e.args[0] == errno.EINVAL:
-                                     print("Function:", fname, ",", e.strerror, file=sys.stderr)
-                                     sys.exit(2)
-                                 raise e
-@@ -364,9 +364,9 @@ def isolate_cpus(cpus, nr_cpus):
-                         try:
-                                 affinity = schedutils.get_affinity(tid)
-                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                if e[0] == errno.ESRCH:
-+                                if e.args[0] == errno.ESRCH:
-                                         continue
--                                elif e[0] == errno.EINVAL:
-+                                elif e.args[0] == errno.EINVAL:
-                                     print("Function:", fname, ",", e.strerror, file=sys.stderr)
-                                     sys.exit(2)
-                                 raise e
-@@ -376,9 +376,9 @@ def isolate_cpus(cpus, nr_cpus):
-                                 try:
-                                         schedutils.set_affinity(tid, affinity)
-                                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                        if e[0] == errno.ESRCH:
-+                                        if e.args[0] == errno.ESRCH:
-                                                 continue
--                                        elif e[0] == errno.EINVAL:
-+                                        elif e.args[0] == errno.EINVAL:
-                                             print("Function:", fname, ",", e.strerror, file=sys.stderr)
-                                             sys.exit(2)
-                                         raise e
-@@ -416,7 +416,7 @@ def include_cpus(cpus, nr_cpus):
-                 try:
-                         affinity = schedutils.get_affinity(pid)
-                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                        if e[0] == errno.ESRCH:
-+                        if e.args[0] == errno.ESRCH:
-                                 continue
-                         raise e
-                 if set(affinity).intersection(set(cpus)) != set(cpus):
-@@ -425,7 +425,7 @@ def include_cpus(cpus, nr_cpus):
-                         try:
-                                 schedutils.set_affinity(pid, affinity)
-                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                if e[0] == errno.ESRCH:
-+                                if e.args[0] == errno.ESRCH:
-                                         continue
-                                 raise e
- 
-@@ -438,7 +438,7 @@ def include_cpus(cpus, nr_cpus):
-                         try:
-                                 affinity = schedutils.get_affinity(tid)
-                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                if e[0] == errno.ESRCH:
-+                                if e.args[0] == errno.ESRCH:
-                                         continue
-                                 raise e
-                         if set(affinity).intersection(set(cpus)) != set(cpus):
-@@ -447,7 +447,7 @@ def include_cpus(cpus, nr_cpus):
-                                 try:
-                                         schedutils.set_affinity(tid, affinity)
-                                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                        if e[0] == errno.ESRCH:
-+                                        if e.args[0] == errno.ESRCH:
-                                                 continue
-                                         raise e
- 
-@@ -515,7 +515,7 @@ def thread_filtered(tid, cpus_filtered, show_kthreads, show_uthreads):
-                 try:
-                         affinity = schedutils.get_affinity(tid)
-                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                        if e[0] == errno.ESRCH:
-+                        if e.args[0] == errno.ESRCH:
-                                 return False
-                         raise e
- 
-@@ -554,7 +554,7 @@ def threads_set_priority(tids, parm, affect_children = False):
-                 try:
-                         thread_set_priority(tid, policy, rtprio)
-                 except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                        if e[0] == errno.ESRCH:
-+                        if e.args[0] == errno.ESRCH:
-                                 continue
-                         raise e
-                 if affect_children:
-@@ -563,7 +563,7 @@ def threads_set_priority(tids, parm, affect_children = False):
-                                         try:
-                                                 thread_set_priority(child, policy, rtprio)
-                                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                                if e[0] == errno.ESRCH:
-+                                                if e.args[0] == errno.ESRCH:
-                                                         continue
-                                                 raise e
- 
-@@ -594,7 +594,7 @@ def get_kthread_sched_tunings(proc = None):
-                                 policy = schedutils.get_scheduler(pid)
-                                 affinity = schedutils.get_affinity(pid)
-                         except (SystemError, OSError) as e: # old python-schedutils incorrectly raised SystemError
--                                if e[0] == errno.ESRCH:
-+                                if e.args[0] == errno.ESRCH:
-                                         continue
-                                 raise e
-                         percpu = iskthread(pid) and \
--- 
-2.19.2
-
diff --git a/SOURCES/tuna-fix-undefined-global-name-stderr.patch b/SOURCES/tuna-fix-undefined-global-name-stderr.patch
deleted file mode 100644
index d200a00..0000000
--- a/SOURCES/tuna-fix-undefined-global-name-stderr.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 38021aa69c811478ddf6e091353b704adb80985a Mon Sep 17 00:00:00 2001
-From: John Kacur <jkacur@redhat.com>
-Date: Fri, 1 Feb 2019 13:57:52 +0100
-Subject: [PATCH] tuna: fix undefined global name stderr
-
-Fix undefined global name stderr. Should be sys.stderr
-
-Signed-off-by: John Kacur <jkacur@redhat.com>
----
- tuna/tuna.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/tuna/tuna.py b/tuna/tuna.py
-index 4b1a77bcfa9f..d1d9bad661c7 100755
---- a/tuna/tuna.py
-+++ b/tuna/tuna.py
-@@ -255,7 +255,7 @@ def move_threads_to_cpu(cpus, pid_list, set_affinity_warning = None,
-                                 # process died
-                                 continue
-                         elif e.args[0] == errno.EINVAL: # unmovable thread)
--                                print("thread %(pid)d cannot be moved as requested" %{'pid':pid}, file=stderr)
-+                                print("thread %(pid)d cannot be moved as requested" %{'pid':pid}, file=sys.stderr)
-                                 continue
-                         raise e
-         return changed
--- 
-2.20.1
-
diff --git a/SOURCES/tuna-sysfs.py-Add-method-to-compare-class-cpu.patch b/SOURCES/tuna-sysfs.py-Add-method-to-compare-class-cpu.patch
deleted file mode 100644
index d6030c0..0000000
--- a/SOURCES/tuna-sysfs.py-Add-method-to-compare-class-cpu.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From feb9d98bb0ed49ed5b9c2977700e9144b5707aa0 Mon Sep 17 00:00:00 2001
-From: John Kacur <jkacur@redhat.com>
-Date: Tue, 4 Dec 2018 00:29:28 +0100
-Subject: [PATCH 1/2] tuna: sysfs.py: Add method to compare class cpu
-
-In python3 you have to supply a method to compare class instances
-
-Without this you get this kind of error
-
-$ python3 sysfs.py
-Traceback (most recent call last):
-  File "sysfs.py", line 97, in <module>
-    cpus = cpus()
-  File "sysfs.py", line 57, in __init__
-    self.reload()
-  File "sysfs.py", line 92, in reload
-    self.sockets[socket].sort()
-TypeError: '<' not supported between instances of 'cpu' and 'cpu'
-
-Signed-off-by: John Kacur <jkacur@redhat.com>
----
- tuna/sysfs.py | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/tuna/sysfs.py b/tuna/sysfs.py
-index acc81d667753..8b8a988659ce 100755
---- a/tuna/sysfs.py
-+++ b/tuna/sysfs.py
-@@ -9,6 +9,9 @@ class cpu:
- 		self.dir = "%s/%s" % (basedir, name)
- 		self.reload()
- 
-+	def __lt__(self, other):
-+		self.name < other.name
-+
- 	def readfile(self, name):
- 		try:
- 			f = open("%s/%s" % (self.dir, name))
--- 
-2.19.2
-
diff --git a/SPECS/tuna.spec b/SPECS/tuna.spec
index f13c387..7dcf56e 100644
--- a/SPECS/tuna.spec
+++ b/SPECS/tuna.spec
@@ -1,28 +1,21 @@
 Name: tuna
-Version: 0.14
-Release: 4%{?dist}
+Version: 0.15
+Release: 1%{?dist}
 License: GPLv2
 Summary: Application tuning GUI & command line utility
 Group: Applications/System
-Source: https://git.kernel.org/pub/scm/utils/tuna/tuna.git/%{name}/%{name}-%{version}.tar.xz
+Source: https://www.kernel.org/pub/software/utils/tuna/%{name}-%{version}.tar.xz
 URL: https://git.kernel.org/pub/scm/utils/tuna/tuna.git
-# If upstream does not provide tarballs, to generate
-# git clone git://git.kernel.org/pub/scm/utils/tuna/tuna.git
-# cd tuna
-# git archive --format=tar --prefix=tuna-%%{version}/ v%%{version} | xz -c > tuna-%%{version}.tar.xz
+
 BuildArch: noarch
 BuildRequires: python3-devel, gettext
 Requires: python3-ethtool
 Requires: python3-linux-procfs >= 0.6
-Requires: python3-schedutils >= 0.6
 # This really should be a Suggests...
 # Requires: python-inet_diag
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 # PATCHES
-Patch1: tuna-sysfs.py-Add-method-to-compare-class-cpu.patch
-Patch2: tuna-Use-exception-args-attribute-for-python3.patch
-Patch3: tuna-fix-undefined-global-name-stderr.patch
 
 %description
 Provides interface for changing scheduler and IRQ tunables, at whole CPU and at
@@ -35,9 +28,6 @@ installed.
 
 %prep
 %setup -q
-%patch1 -p1
-%patch2 -p1
-%patch3 -p1
 
 %build
 %{__python3} setup.py build
@@ -84,6 +74,11 @@ rm -rf %{buildroot}
 %{_datadir}/polkit-1/actions/org.tuna.policy
 
 %changelog
+* Thu Jan 21 2021 John Kacur <jkacur@redhat.com> - 0.15-1
+- Upgrade to latest upstream code
+- Upstream drops python-schedutils and uses built-in schedutils
+Resolves: rhbz#1890558
+
 * Tue Apr 02 2019 Clark Williams <williams@redhat.com> - 0.14-4
 - added OSCI gating framework
 Resolves: rhbz#1682423