mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 07:55:15 +07:00
b24413180f
Many source files in the tree are missing licensing information, which makes it harder for compliance tools to determine the correct license. By default all files without license information are under the default license of the kernel, which is GPL version 2. Update the files which contain no license information with the 'GPL-2.0' SPDX license identifier. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This patch is based on work done by Thomas Gleixner and Kate Stewart and Philippe Ombredanne. How this work was done: Patches were generated and checked against linux-4.14-rc6 for a subset of the use cases: - file had no licensing information it it. - file was a */uapi/* one with no licensing information in it, - file was a */uapi/* one with existing licensing information, Further patches will be generated in subsequent months to fix up cases where non-standard license headers were used, and references to license had to be inferred by heuristics based on keywords. The analysis to determine which SPDX License Identifier to be applied to a file was done in a spreadsheet of side by side results from of the output of two independent scanners (ScanCode & Windriver) producing SPDX tag:value files created by Philippe Ombredanne. Philippe prepared the base worksheet, and did an initial spot review of a few 1000 files. The 4.13 kernel was the starting point of the analysis with 60,537 files assessed. Kate Stewart did a file by file comparison of the scanner results in the spreadsheet to determine which SPDX license identifier(s) to be applied to the file. She confirmed any determination that was not immediately clear with lawyers working with the Linux Foundation. Criteria used to select files for SPDX license identifier tagging was: - Files considered eligible had to be source code files. - Make and config files were included as candidates if they contained >5 lines of source - File already had some variant of a license header in it (even if <5 lines). All documentation files were explicitly excluded. The following heuristics were used to determine which SPDX license identifiers to apply. - when both scanners couldn't find any license traces, file was considered to have no license information in it, and the top level COPYING file license applied. For non */uapi/* files that summary was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 11139 and resulted in the first patch in this series. If that file was a */uapi/* path one, it was "GPL-2.0 WITH Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 WITH Linux-syscall-note 930 and resulted in the second patch in this series. - if a file had some form of licensing information in it, and was one of the */uapi/* ones, it was denoted with the Linux-syscall-note if any GPL family license was found in the file or had no licensing in it (per prior point). Results summary: SPDX license identifier # files ---------------------------------------------------|------ GPL-2.0 WITH Linux-syscall-note 270 GPL-2.0+ WITH Linux-syscall-note 169 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17 LGPL-2.1+ WITH Linux-syscall-note 15 GPL-1.0+ WITH Linux-syscall-note 14 ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5 LGPL-2.0+ WITH Linux-syscall-note 4 LGPL-2.1 WITH Linux-syscall-note 3 ((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3 ((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1 and that resulted in the third patch in this series. - when the two scanners agreed on the detected license(s), that became the concluded license(s). - when there was disagreement between the two scanners (one detected a license but the other didn't, or they both detected different licenses) a manual inspection of the file occurred. - In most cases a manual inspection of the information in the file resulted in a clear resolution of the license that should apply (and which scanner probably needed to revisit its heuristics). - When it was not immediately clear, the license identifier was confirmed with lawyers working with the Linux Foundation. - If there was any question as to the appropriate license identifier, the file was flagged for further research and to be revisited later in time. In total, over 70 hours of logged manual review was done on the spreadsheet to determine the SPDX license identifiers to apply to the source files by Kate, Philippe, Thomas and, in some cases, confirmation by lawyers working with the Linux Foundation. Kate also obtained a third independent scan of the 4.13 code base from FOSSology, and compared selected files where the other two scanners disagreed against that SPDX file, to see if there was new insights. The Windriver scanner is based on an older version of FOSSology in part, so they are related. Thomas did random spot checks in about 500 files from the spreadsheets for the uapi headers and agreed with SPDX license identifier in the files he inspected. For the non-uapi files Thomas did random spot checks in about 15000 files. In initial set of patches against 4.14-rc6, 3 files were found to have copy/paste license identifier errors, and have been fixed to reflect the correct identifier. Additionally Philippe spent 10 hours this week doing a detailed manual inspection and review of the 12,461 patched files from the initial patch version early this week with: - a full scancode scan run, collecting the matched texts, detected license ids and scores - reviewing anything where there was a license detected (about 500+ files) to ensure that the applied SPDX license was correct - reviewing anything where there was no detection but the patch license was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied SPDX license was correct This produced a worksheet with 20 files needing minor correction. This worksheet was then exported into 3 different .csv files for the different types of files to be modified. These .csv files were then reviewed by Greg. Thomas wrote a script to parse the csv files and add the proper SPDX tag to the file, in the format that the file expected. This script was further refined by Greg based on the output to detect more types of files automatically and to distinguish between header and source .c files (which need different comment types.) Finally Greg ran the script using the .csv files to generate the patches. Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
405 lines
11 KiB
C
405 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* kernel/sched/loadavg.c
|
|
*
|
|
* This file contains the magic bits required to compute the global loadavg
|
|
* figure. Its a silly number but people think its important. We go through
|
|
* great pains to make it work on big machines and tickless kernels.
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/sched/loadavg.h>
|
|
|
|
#include "sched.h"
|
|
|
|
/*
|
|
* Global load-average calculations
|
|
*
|
|
* We take a distributed and async approach to calculating the global load-avg
|
|
* in order to minimize overhead.
|
|
*
|
|
* The global load average is an exponentially decaying average of nr_running +
|
|
* nr_uninterruptible.
|
|
*
|
|
* Once every LOAD_FREQ:
|
|
*
|
|
* nr_active = 0;
|
|
* for_each_possible_cpu(cpu)
|
|
* nr_active += cpu_of(cpu)->nr_running + cpu_of(cpu)->nr_uninterruptible;
|
|
*
|
|
* avenrun[n] = avenrun[0] * exp_n + nr_active * (1 - exp_n)
|
|
*
|
|
* Due to a number of reasons the above turns in the mess below:
|
|
*
|
|
* - for_each_possible_cpu() is prohibitively expensive on machines with
|
|
* serious number of cpus, therefore we need to take a distributed approach
|
|
* to calculating nr_active.
|
|
*
|
|
* \Sum_i x_i(t) = \Sum_i x_i(t) - x_i(t_0) | x_i(t_0) := 0
|
|
* = \Sum_i { \Sum_j=1 x_i(t_j) - x_i(t_j-1) }
|
|
*
|
|
* So assuming nr_active := 0 when we start out -- true per definition, we
|
|
* can simply take per-cpu deltas and fold those into a global accumulate
|
|
* to obtain the same result. See calc_load_fold_active().
|
|
*
|
|
* Furthermore, in order to avoid synchronizing all per-cpu delta folding
|
|
* across the machine, we assume 10 ticks is sufficient time for every
|
|
* cpu to have completed this task.
|
|
*
|
|
* This places an upper-bound on the IRQ-off latency of the machine. Then
|
|
* again, being late doesn't loose the delta, just wrecks the sample.
|
|
*
|
|
* - cpu_rq()->nr_uninterruptible isn't accurately tracked per-cpu because
|
|
* this would add another cross-cpu cacheline miss and atomic operation
|
|
* to the wakeup path. Instead we increment on whatever cpu the task ran
|
|
* when it went into uninterruptible state and decrement on whatever cpu
|
|
* did the wakeup. This means that only the sum of nr_uninterruptible over
|
|
* all cpus yields the correct result.
|
|
*
|
|
* This covers the NO_HZ=n code, for extra head-aches, see the comment below.
|
|
*/
|
|
|
|
/* Variables and functions for calc_load */
|
|
atomic_long_t calc_load_tasks;
|
|
unsigned long calc_load_update;
|
|
unsigned long avenrun[3];
|
|
EXPORT_SYMBOL(avenrun); /* should be removed */
|
|
|
|
/**
|
|
* get_avenrun - get the load average array
|
|
* @loads: pointer to dest load array
|
|
* @offset: offset to add
|
|
* @shift: shift count to shift the result left
|
|
*
|
|
* These values are estimates at best, so no need for locking.
|
|
*/
|
|
void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
|
|
{
|
|
loads[0] = (avenrun[0] + offset) << shift;
|
|
loads[1] = (avenrun[1] + offset) << shift;
|
|
loads[2] = (avenrun[2] + offset) << shift;
|
|
}
|
|
|
|
long calc_load_fold_active(struct rq *this_rq, long adjust)
|
|
{
|
|
long nr_active, delta = 0;
|
|
|
|
nr_active = this_rq->nr_running - adjust;
|
|
nr_active += (long)this_rq->nr_uninterruptible;
|
|
|
|
if (nr_active != this_rq->calc_load_active) {
|
|
delta = nr_active - this_rq->calc_load_active;
|
|
this_rq->calc_load_active = nr_active;
|
|
}
|
|
|
|
return delta;
|
|
}
|
|
|
|
/*
|
|
* a1 = a0 * e + a * (1 - e)
|
|
*/
|
|
static unsigned long
|
|
calc_load(unsigned long load, unsigned long exp, unsigned long active)
|
|
{
|
|
unsigned long newload;
|
|
|
|
newload = load * exp + active * (FIXED_1 - exp);
|
|
if (active >= load)
|
|
newload += FIXED_1-1;
|
|
|
|
return newload / FIXED_1;
|
|
}
|
|
|
|
#ifdef CONFIG_NO_HZ_COMMON
|
|
/*
|
|
* Handle NO_HZ for the global load-average.
|
|
*
|
|
* Since the above described distributed algorithm to compute the global
|
|
* load-average relies on per-cpu sampling from the tick, it is affected by
|
|
* NO_HZ.
|
|
*
|
|
* The basic idea is to fold the nr_active delta into a global NO_HZ-delta upon
|
|
* entering NO_HZ state such that we can include this as an 'extra' cpu delta
|
|
* when we read the global state.
|
|
*
|
|
* Obviously reality has to ruin such a delightfully simple scheme:
|
|
*
|
|
* - When we go NO_HZ idle during the window, we can negate our sample
|
|
* contribution, causing under-accounting.
|
|
*
|
|
* We avoid this by keeping two NO_HZ-delta counters and flipping them
|
|
* when the window starts, thus separating old and new NO_HZ load.
|
|
*
|
|
* The only trick is the slight shift in index flip for read vs write.
|
|
*
|
|
* 0s 5s 10s 15s
|
|
* +10 +10 +10 +10
|
|
* |-|-----------|-|-----------|-|-----------|-|
|
|
* r:0 0 1 1 0 0 1 1 0
|
|
* w:0 1 1 0 0 1 1 0 0
|
|
*
|
|
* This ensures we'll fold the old NO_HZ contribution in this window while
|
|
* accumlating the new one.
|
|
*
|
|
* - When we wake up from NO_HZ during the window, we push up our
|
|
* contribution, since we effectively move our sample point to a known
|
|
* busy state.
|
|
*
|
|
* This is solved by pushing the window forward, and thus skipping the
|
|
* sample, for this cpu (effectively using the NO_HZ-delta for this cpu which
|
|
* was in effect at the time the window opened). This also solves the issue
|
|
* of having to deal with a cpu having been in NO_HZ for multiple LOAD_FREQ
|
|
* intervals.
|
|
*
|
|
* When making the ILB scale, we should try to pull this in as well.
|
|
*/
|
|
static atomic_long_t calc_load_nohz[2];
|
|
static int calc_load_idx;
|
|
|
|
static inline int calc_load_write_idx(void)
|
|
{
|
|
int idx = calc_load_idx;
|
|
|
|
/*
|
|
* See calc_global_nohz(), if we observe the new index, we also
|
|
* need to observe the new update time.
|
|
*/
|
|
smp_rmb();
|
|
|
|
/*
|
|
* If the folding window started, make sure we start writing in the
|
|
* next NO_HZ-delta.
|
|
*/
|
|
if (!time_before(jiffies, READ_ONCE(calc_load_update)))
|
|
idx++;
|
|
|
|
return idx & 1;
|
|
}
|
|
|
|
static inline int calc_load_read_idx(void)
|
|
{
|
|
return calc_load_idx & 1;
|
|
}
|
|
|
|
void calc_load_nohz_start(void)
|
|
{
|
|
struct rq *this_rq = this_rq();
|
|
long delta;
|
|
|
|
/*
|
|
* We're going into NO_HZ mode, if there's any pending delta, fold it
|
|
* into the pending NO_HZ delta.
|
|
*/
|
|
delta = calc_load_fold_active(this_rq, 0);
|
|
if (delta) {
|
|
int idx = calc_load_write_idx();
|
|
|
|
atomic_long_add(delta, &calc_load_nohz[idx]);
|
|
}
|
|
}
|
|
|
|
void calc_load_nohz_stop(void)
|
|
{
|
|
struct rq *this_rq = this_rq();
|
|
|
|
/*
|
|
* If we're still before the pending sample window, we're done.
|
|
*/
|
|
this_rq->calc_load_update = READ_ONCE(calc_load_update);
|
|
if (time_before(jiffies, this_rq->calc_load_update))
|
|
return;
|
|
|
|
/*
|
|
* We woke inside or after the sample window, this means we're already
|
|
* accounted through the nohz accounting, so skip the entire deal and
|
|
* sync up for the next window.
|
|
*/
|
|
if (time_before(jiffies, this_rq->calc_load_update + 10))
|
|
this_rq->calc_load_update += LOAD_FREQ;
|
|
}
|
|
|
|
static long calc_load_nohz_fold(void)
|
|
{
|
|
int idx = calc_load_read_idx();
|
|
long delta = 0;
|
|
|
|
if (atomic_long_read(&calc_load_nohz[idx]))
|
|
delta = atomic_long_xchg(&calc_load_nohz[idx], 0);
|
|
|
|
return delta;
|
|
}
|
|
|
|
/**
|
|
* fixed_power_int - compute: x^n, in O(log n) time
|
|
*
|
|
* @x: base of the power
|
|
* @frac_bits: fractional bits of @x
|
|
* @n: power to raise @x to.
|
|
*
|
|
* By exploiting the relation between the definition of the natural power
|
|
* function: x^n := x*x*...*x (x multiplied by itself for n times), and
|
|
* the binary encoding of numbers used by computers: n := \Sum n_i * 2^i,
|
|
* (where: n_i \elem {0, 1}, the binary vector representing n),
|
|
* we find: x^n := x^(\Sum n_i * 2^i) := \Prod x^(n_i * 2^i), which is
|
|
* of course trivially computable in O(log_2 n), the length of our binary
|
|
* vector.
|
|
*/
|
|
static unsigned long
|
|
fixed_power_int(unsigned long x, unsigned int frac_bits, unsigned int n)
|
|
{
|
|
unsigned long result = 1UL << frac_bits;
|
|
|
|
if (n) {
|
|
for (;;) {
|
|
if (n & 1) {
|
|
result *= x;
|
|
result += 1UL << (frac_bits - 1);
|
|
result >>= frac_bits;
|
|
}
|
|
n >>= 1;
|
|
if (!n)
|
|
break;
|
|
x *= x;
|
|
x += 1UL << (frac_bits - 1);
|
|
x >>= frac_bits;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* a1 = a0 * e + a * (1 - e)
|
|
*
|
|
* a2 = a1 * e + a * (1 - e)
|
|
* = (a0 * e + a * (1 - e)) * e + a * (1 - e)
|
|
* = a0 * e^2 + a * (1 - e) * (1 + e)
|
|
*
|
|
* a3 = a2 * e + a * (1 - e)
|
|
* = (a0 * e^2 + a * (1 - e) * (1 + e)) * e + a * (1 - e)
|
|
* = a0 * e^3 + a * (1 - e) * (1 + e + e^2)
|
|
*
|
|
* ...
|
|
*
|
|
* an = a0 * e^n + a * (1 - e) * (1 + e + ... + e^n-1) [1]
|
|
* = a0 * e^n + a * (1 - e) * (1 - e^n)/(1 - e)
|
|
* = a0 * e^n + a * (1 - e^n)
|
|
*
|
|
* [1] application of the geometric series:
|
|
*
|
|
* n 1 - x^(n+1)
|
|
* S_n := \Sum x^i = -------------
|
|
* i=0 1 - x
|
|
*/
|
|
static unsigned long
|
|
calc_load_n(unsigned long load, unsigned long exp,
|
|
unsigned long active, unsigned int n)
|
|
{
|
|
return calc_load(load, fixed_power_int(exp, FSHIFT, n), active);
|
|
}
|
|
|
|
/*
|
|
* NO_HZ can leave us missing all per-cpu ticks calling
|
|
* calc_load_fold_active(), but since a NO_HZ CPU folds its delta into
|
|
* calc_load_nohz per calc_load_nohz_start(), all we need to do is fold
|
|
* in the pending NO_HZ delta if our NO_HZ period crossed a load cycle boundary.
|
|
*
|
|
* Once we've updated the global active value, we need to apply the exponential
|
|
* weights adjusted to the number of cycles missed.
|
|
*/
|
|
static void calc_global_nohz(void)
|
|
{
|
|
unsigned long sample_window;
|
|
long delta, active, n;
|
|
|
|
sample_window = READ_ONCE(calc_load_update);
|
|
if (!time_before(jiffies, sample_window + 10)) {
|
|
/*
|
|
* Catch-up, fold however many we are behind still
|
|
*/
|
|
delta = jiffies - sample_window - 10;
|
|
n = 1 + (delta / LOAD_FREQ);
|
|
|
|
active = atomic_long_read(&calc_load_tasks);
|
|
active = active > 0 ? active * FIXED_1 : 0;
|
|
|
|
avenrun[0] = calc_load_n(avenrun[0], EXP_1, active, n);
|
|
avenrun[1] = calc_load_n(avenrun[1], EXP_5, active, n);
|
|
avenrun[2] = calc_load_n(avenrun[2], EXP_15, active, n);
|
|
|
|
WRITE_ONCE(calc_load_update, sample_window + n * LOAD_FREQ);
|
|
}
|
|
|
|
/*
|
|
* Flip the NO_HZ index...
|
|
*
|
|
* Make sure we first write the new time then flip the index, so that
|
|
* calc_load_write_idx() will see the new time when it reads the new
|
|
* index, this avoids a double flip messing things up.
|
|
*/
|
|
smp_wmb();
|
|
calc_load_idx++;
|
|
}
|
|
#else /* !CONFIG_NO_HZ_COMMON */
|
|
|
|
static inline long calc_load_nohz_fold(void) { return 0; }
|
|
static inline void calc_global_nohz(void) { }
|
|
|
|
#endif /* CONFIG_NO_HZ_COMMON */
|
|
|
|
/*
|
|
* calc_load - update the avenrun load estimates 10 ticks after the
|
|
* CPUs have updated calc_load_tasks.
|
|
*
|
|
* Called from the global timer code.
|
|
*/
|
|
void calc_global_load(unsigned long ticks)
|
|
{
|
|
unsigned long sample_window;
|
|
long active, delta;
|
|
|
|
sample_window = READ_ONCE(calc_load_update);
|
|
if (time_before(jiffies, sample_window + 10))
|
|
return;
|
|
|
|
/*
|
|
* Fold the 'old' NO_HZ-delta to include all NO_HZ cpus.
|
|
*/
|
|
delta = calc_load_nohz_fold();
|
|
if (delta)
|
|
atomic_long_add(delta, &calc_load_tasks);
|
|
|
|
active = atomic_long_read(&calc_load_tasks);
|
|
active = active > 0 ? active * FIXED_1 : 0;
|
|
|
|
avenrun[0] = calc_load(avenrun[0], EXP_1, active);
|
|
avenrun[1] = calc_load(avenrun[1], EXP_5, active);
|
|
avenrun[2] = calc_load(avenrun[2], EXP_15, active);
|
|
|
|
WRITE_ONCE(calc_load_update, sample_window + LOAD_FREQ);
|
|
|
|
/*
|
|
* In case we went to NO_HZ for multiple LOAD_FREQ intervals
|
|
* catch up in bulk.
|
|
*/
|
|
calc_global_nohz();
|
|
}
|
|
|
|
/*
|
|
* Called from scheduler_tick() to periodically update this CPU's
|
|
* active count.
|
|
*/
|
|
void calc_global_load_tick(struct rq *this_rq)
|
|
{
|
|
long delta;
|
|
|
|
if (time_before(jiffies, this_rq->calc_load_update))
|
|
return;
|
|
|
|
delta = calc_load_fold_active(this_rq, 0);
|
|
if (delta)
|
|
atomic_long_add(delta, &calc_load_tasks);
|
|
|
|
this_rq->calc_load_update += LOAD_FREQ;
|
|
}
|