summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/runqueue.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/runqueue.py')
-rw-r--r--bitbake/lib/bb/runqueue.py65
1 files changed, 65 insertions, 0 deletions
diff --git a/bitbake/lib/bb/runqueue.py b/bitbake/lib/bb/runqueue.py
index 1e47fe70ef..359b503297 100644
--- a/bitbake/lib/bb/runqueue.py
+++ b/bitbake/lib/bb/runqueue.py
@@ -24,6 +24,7 @@ import pickle
24from multiprocessing import Process 24from multiprocessing import Process
25import shlex 25import shlex
26import pprint 26import pprint
27import time
27 28
28bblogger = logging.getLogger("BitBake") 29bblogger = logging.getLogger("BitBake")
29logger = logging.getLogger("BitBake.RunQueue") 30logger = logging.getLogger("BitBake.RunQueue")
@@ -159,6 +160,46 @@ class RunQueueScheduler(object):
159 self.buildable.append(tid) 160 self.buildable.append(tid)
160 161
161 self.rev_prio_map = None 162 self.rev_prio_map = None
163 self.is_pressure_usable()
164
165 def is_pressure_usable(self):
166 """
167 If monitoring pressure, return True if pressure files can be open and read. For example
168 openSUSE /proc/pressure/* files have readable file permissions but when read the error EOPNOTSUPP (Operation not supported)
169 is returned.
170 """
171 if self.rq.max_cpu_pressure or self.rq.max_io_pressure:
172 try:
173 with open("/proc/pressure/cpu") as cpu_pressure_fds, open("/proc/pressure/io") as io_pressure_fds:
174 self.prev_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
175 self.prev_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
176 self.prev_pressure_time = time.time()
177 self.check_pressure = True
178 except:
179 bb.warn("The /proc/pressure files can't be read. Continuing build without monitoring pressure")
180 self.check_pressure = False
181 else:
182 self.check_pressure = False
183
184 def exceeds_max_pressure(self):
185 """
186 Monitor the difference in total pressure at least once per second, if
187 BB_PRESSURE_MAX_{CPU|IO} are set, return True if above threshold.
188 """
189 if self.check_pressure:
190 with open("/proc/pressure/cpu") as cpu_pressure_fds, open("/proc/pressure/io") as io_pressure_fds:
191 # extract "total" from /proc/pressure/{cpu|io}
192 curr_cpu_pressure = cpu_pressure_fds.readline().split()[4].split("=")[1]
193 curr_io_pressure = io_pressure_fds.readline().split()[4].split("=")[1]
194 exceeds_cpu_pressure = self.rq.max_cpu_pressure and (float(curr_cpu_pressure) - float(self.prev_cpu_pressure)) > self.rq.max_cpu_pressure
195 exceeds_io_pressure = self.rq.max_io_pressure and (float(curr_io_pressure) - float(self.prev_io_pressure)) > self.rq.max_io_pressure
196 now = time.time()
197 if now - self.prev_pressure_time > 1.0:
198 self.prev_cpu_pressure = curr_cpu_pressure
199 self.prev_io_pressure = curr_io_pressure
200 self.prev_pressure_time = now
201 return (exceeds_cpu_pressure or exceeds_io_pressure)
202 return False
162 203
163 def next_buildable_task(self): 204 def next_buildable_task(self):
164 """ 205 """
@@ -172,6 +213,12 @@ class RunQueueScheduler(object):
172 if not buildable: 213 if not buildable:
173 return None 214 return None
174 215
216 # Bitbake requires that at least one task be active. Only check for pressure if
217 # this is the case, otherwise the pressure limitation could result in no tasks
218 # being active and no new tasks started thereby, at times, breaking the scheduler.
219 if self.rq.stats.active and self.exceeds_max_pressure():
220 return None
221
175 # Filter out tasks that have a max number of threads that have been exceeded 222 # Filter out tasks that have a max number of threads that have been exceeded
176 skip_buildable = {} 223 skip_buildable = {}
177 for running in self.rq.runq_running.difference(self.rq.runq_complete): 224 for running in self.rq.runq_running.difference(self.rq.runq_complete):
@@ -1699,6 +1746,8 @@ class RunQueueExecute:
1699 1746
1700 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1) 1747 self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
1701 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed" 1748 self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
1749 self.max_cpu_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_CPU")
1750 self.max_io_pressure = self.cfgData.getVar("BB_PRESSURE_MAX_IO")
1702 1751
1703 self.sq_buildable = set() 1752 self.sq_buildable = set()
1704 self.sq_running = set() 1753 self.sq_running = set()
@@ -1733,6 +1782,22 @@ class RunQueueExecute:
1733 if self.number_tasks <= 0: 1782 if self.number_tasks <= 0:
1734 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks) 1783 bb.fatal("Invalid BB_NUMBER_THREADS %s" % self.number_tasks)
1735 1784
1785 lower_limit = 1.0
1786 upper_limit = 1000000.0
1787 if self.max_cpu_pressure:
1788 self.max_cpu_pressure = float(self.max_cpu_pressure)
1789 if self.max_cpu_pressure < lower_limit:
1790 bb.fatal("Invalid BB_PRESSURE_MAX_CPU %s, minimum value is %s." % (self.max_cpu_pressure, lower_limit))
1791 if self.max_cpu_pressure > upper_limit:
1792 bb.warn("Your build will be largely unregulated since BB_PRESSURE_MAX_CPU is set to %s. It is very unlikely that such high pressure will be experienced." % (self.max_cpu_pressure))
1793
1794 if self.max_io_pressure:
1795 self.max_io_pressure = float(self.max_io_pressure)
1796 if self.max_io_pressure < lower_limit:
1797 bb.fatal("Invalid BB_PRESSURE_MAX_IO %s, minimum value is %s." % (self.max_io_pressure, lower_limit))
1798 if self.max_io_pressure > upper_limit:
1799 bb.warn("Your build will be largely unregulated since BB_PRESSURE_MAX_IO is set to %s. It is very unlikely that such high pressure will be experienced." % (self.max_io_pressure))
1800
1736 # List of setscene tasks which we've covered 1801 # List of setscene tasks which we've covered
1737 self.scenequeue_covered = set() 1802 self.scenequeue_covered = set()
1738 # List of tasks which are covered (including setscene ones) 1803 # List of tasks which are covered (including setscene ones)