18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2016, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 38c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "irq_timings: " fmt 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/kernel.h> 68c2ecf20Sopenharmony_ci#include <linux/percpu.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/static_key.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/idr.h> 128c2ecf20Sopenharmony_ci#include <linux/irq.h> 138c2ecf20Sopenharmony_ci#include <linux/math64.h> 148c2ecf20Sopenharmony_ci#include <linux/log2.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <trace/events/irq.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "internals.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(irq_timing_enabled); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciDEFINE_PER_CPU(struct irq_timings, irq_timings); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic DEFINE_IDR(irqt_stats); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_civoid irq_timings_enable(void) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci static_branch_enable(&irq_timing_enabled); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_civoid irq_timings_disable(void) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci static_branch_disable(&irq_timing_enabled); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * The main goal of this algorithm is to predict the next interrupt 388c2ecf20Sopenharmony_ci * occurrence on the current CPU. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Currently, the interrupt timings are stored in a circular array 418c2ecf20Sopenharmony_ci * buffer every time there is an interrupt, as a tuple: the interrupt 428c2ecf20Sopenharmony_ci * number and the associated timestamp when the event occurred <irq, 438c2ecf20Sopenharmony_ci * timestamp>. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * For every interrupt occurring in a short period of time, we can 468c2ecf20Sopenharmony_ci * measure the elapsed time between the occurrences for the same 478c2ecf20Sopenharmony_ci * interrupt and we end up with a suite of intervals. The experience 488c2ecf20Sopenharmony_ci * showed the interrupts are often coming following a periodic 498c2ecf20Sopenharmony_ci * pattern. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * The objective of the algorithm is to find out this periodic pattern 528c2ecf20Sopenharmony_ci * in a fastest way and use its period to predict the next irq event. 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * When the next interrupt event is requested, we are in the situation 558c2ecf20Sopenharmony_ci * where the interrupts are disabled and the circular buffer 568c2ecf20Sopenharmony_ci * containing the timings is filled with the events which happened 578c2ecf20Sopenharmony_ci * after the previous next-interrupt-event request. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * At this point, we read the circular buffer and we fill the irq 608c2ecf20Sopenharmony_ci * related statistics structure. After this step, the circular array 618c2ecf20Sopenharmony_ci * containing the timings is empty because all the values are 628c2ecf20Sopenharmony_ci * dispatched in their corresponding buffers. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Now for each interrupt, we can predict the next event by using the 658c2ecf20Sopenharmony_ci * suffix array, log interval and exponential moving average 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * 1. Suffix array 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * Suffix array is an array of all the suffixes of a string. It is 708c2ecf20Sopenharmony_ci * widely used as a data structure for compression, text search, ... 718c2ecf20Sopenharmony_ci * For instance for the word 'banana', the suffixes will be: 'banana' 728c2ecf20Sopenharmony_ci * 'anana' 'nana' 'ana' 'na' 'a' 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * Usually, the suffix array is sorted but for our purpose it is 758c2ecf20Sopenharmony_ci * not necessary and won't provide any improvement in the context of 768c2ecf20Sopenharmony_ci * the solved problem where we clearly define the boundaries of the 778c2ecf20Sopenharmony_ci * search by a max period and min period. 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * The suffix array will build a suite of intervals of different 808c2ecf20Sopenharmony_ci * length and will look for the repetition of each suite. If the suite 818c2ecf20Sopenharmony_ci * is repeating then we have the period because it is the length of 828c2ecf20Sopenharmony_ci * the suite whatever its position in the buffer. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * 2. Log interval 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * We saw the irq timings allow to compute the interval of the 878c2ecf20Sopenharmony_ci * occurrences for a specific interrupt. We can reasonibly assume the 888c2ecf20Sopenharmony_ci * longer is the interval, the higher is the error for the next event 898c2ecf20Sopenharmony_ci * and we can consider storing those interval values into an array 908c2ecf20Sopenharmony_ci * where each slot in the array correspond to an interval at the power 918c2ecf20Sopenharmony_ci * of 2 of the index. For example, index 12 will contain values 928c2ecf20Sopenharmony_ci * between 2^11 and 2^12. 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * At the end we have an array of values where at each index defines a 958c2ecf20Sopenharmony_ci * [2^index - 1, 2 ^ index] interval values allowing to store a large 968c2ecf20Sopenharmony_ci * number of values inside a small array. 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * For example, if we have the value 1123, then we store it at 998c2ecf20Sopenharmony_ci * ilog2(1123) = 10 index value. 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Storing those value at the specific index is done by computing an 1028c2ecf20Sopenharmony_ci * exponential moving average for this specific slot. For instance, 1038c2ecf20Sopenharmony_ci * for values 1800, 1123, 1453, ... fall under the same slot (10) and 1048c2ecf20Sopenharmony_ci * the exponential moving average is computed every time a new value 1058c2ecf20Sopenharmony_ci * is stored at this slot. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * 3. Exponential Moving Average 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * The EMA is largely used to track a signal for stocks or as a low 1108c2ecf20Sopenharmony_ci * pass filter. The magic of the formula, is it is very simple and the 1118c2ecf20Sopenharmony_ci * reactivity of the average can be tuned with the factors called 1128c2ecf20Sopenharmony_ci * alpha. 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * The higher the alphas are, the faster the average respond to the 1158c2ecf20Sopenharmony_ci * signal change. In our case, if a slot in the array is a big 1168c2ecf20Sopenharmony_ci * interval, we can have numbers with a big difference between 1178c2ecf20Sopenharmony_ci * them. The impact of those differences in the average computation 1188c2ecf20Sopenharmony_ci * can be tuned by changing the alpha value. 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * -- The algorithm -- 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * We saw the different processing above, now let's see how they are 1248c2ecf20Sopenharmony_ci * used together. 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * For each interrupt: 1278c2ecf20Sopenharmony_ci * For each interval: 1288c2ecf20Sopenharmony_ci * Compute the index = ilog2(interval) 1298c2ecf20Sopenharmony_ci * Compute a new_ema(buffer[index], interval) 1308c2ecf20Sopenharmony_ci * Store the index in a circular buffer 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Compute the suffix array of the indexes 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * For each suffix: 1358c2ecf20Sopenharmony_ci * If the suffix is reverse-found 3 times 1368c2ecf20Sopenharmony_ci * Return suffix 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * Return Not found 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * However we can not have endless suffix array to be build, it won't 1418c2ecf20Sopenharmony_ci * make sense and it will add an extra overhead, so we can restrict 1428c2ecf20Sopenharmony_ci * this to a maximum suffix length of 5 and a minimum suffix length of 1438c2ecf20Sopenharmony_ci * 2. The experience showed 5 is the majority of the maximum pattern 1448c2ecf20Sopenharmony_ci * period found for different devices. 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * The result is a pattern finding less than 1us for an interrupt. 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * Example based on real values: 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * Example 1 : MMC write/read interrupt interval: 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * 223947, 1240, 1384, 1386, 1386, 1538c2ecf20Sopenharmony_ci * 217416, 1236, 1384, 1386, 1387, 1548c2ecf20Sopenharmony_ci * 214719, 1241, 1386, 1387, 1384, 1558c2ecf20Sopenharmony_ci * 213696, 1234, 1384, 1386, 1388, 1568c2ecf20Sopenharmony_ci * 219904, 1240, 1385, 1389, 1385, 1578c2ecf20Sopenharmony_ci * 212240, 1240, 1386, 1386, 1386, 1588c2ecf20Sopenharmony_ci * 214415, 1236, 1384, 1386, 1387, 1598c2ecf20Sopenharmony_ci * 214276, 1234, 1384, 1388, ? 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * For each element, apply ilog2(value) 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1648c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1658c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1668c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1678c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1688c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1698c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1708c2ecf20Sopenharmony_ci * 15, 8, 8, 8, ? 1718c2ecf20Sopenharmony_ci * 1728c2ecf20Sopenharmony_ci * Max period of 5, we take the last (max_period * 3) 15 elements as 1738c2ecf20Sopenharmony_ci * we can be confident if the pattern repeats itself three times it is 1748c2ecf20Sopenharmony_ci * a repeating pattern. 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * 8, 1778c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1788c2ecf20Sopenharmony_ci * 15, 8, 8, 8, 8, 1798c2ecf20Sopenharmony_ci * 15, 8, 8, 8, ? 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * Suffixes are: 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * 1) 8, 15, 8, 8, 8 <- max period 1848c2ecf20Sopenharmony_ci * 2) 8, 15, 8, 8 1858c2ecf20Sopenharmony_ci * 3) 8, 15, 8 1868c2ecf20Sopenharmony_ci * 4) 8, 15 <- min period 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * From there we search the repeating pattern for each suffix. 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * buffer: 8, 15, 8, 8, 8, 8, 15, 8, 8, 8, 8, 15, 8, 8, 8 1918c2ecf20Sopenharmony_ci * | | | | | | | | | | | | | | | 1928c2ecf20Sopenharmony_ci * 8, 15, 8, 8, 8 | | | | | | | | | | 1938c2ecf20Sopenharmony_ci * 8, 15, 8, 8, 8 | | | | | 1948c2ecf20Sopenharmony_ci * 8, 15, 8, 8, 8 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * When moving the suffix, we found exactly 3 matches. 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * The first suffix with period 5 is repeating. 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * The next event is (3 * max_period) % suffix_period 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * In this example, the result 0, so the next event is suffix[0] => 8 2038c2ecf20Sopenharmony_ci * 2048c2ecf20Sopenharmony_ci * However, 8 is the index in the array of exponential moving average 2058c2ecf20Sopenharmony_ci * which was calculated on the fly when storing the values, so the 2068c2ecf20Sopenharmony_ci * interval is ema[8] = 1366 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * Example 2: 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * 4, 3, 5, 100, 2128c2ecf20Sopenharmony_ci * 3, 3, 5, 117, 2138c2ecf20Sopenharmony_ci * 4, 4, 5, 112, 2148c2ecf20Sopenharmony_ci * 4, 3, 4, 110, 2158c2ecf20Sopenharmony_ci * 3, 5, 3, 117, 2168c2ecf20Sopenharmony_ci * 4, 4, 5, 112, 2178c2ecf20Sopenharmony_ci * 4, 3, 4, 110, 2188c2ecf20Sopenharmony_ci * 3, 4, 5, 112, 2198c2ecf20Sopenharmony_ci * 4, 3, 4, 110 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci * ilog2 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2248c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2258c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2268c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2278c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2288c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2298c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2308c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2318c2ecf20Sopenharmony_ci * 0, 0, 0, 4 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Max period 5: 2348c2ecf20Sopenharmony_ci * 0, 0, 4, 2358c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2368c2ecf20Sopenharmony_ci * 0, 0, 0, 4, 2378c2ecf20Sopenharmony_ci * 0, 0, 0, 4 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * Suffixes: 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * 1) 0, 0, 4, 0, 0 2428c2ecf20Sopenharmony_ci * 2) 0, 0, 4, 0 2438c2ecf20Sopenharmony_ci * 3) 0, 0, 4 2448c2ecf20Sopenharmony_ci * 4) 0, 0 2458c2ecf20Sopenharmony_ci * 2468c2ecf20Sopenharmony_ci * buffer: 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4 2478c2ecf20Sopenharmony_ci * | | | | | | X 2488c2ecf20Sopenharmony_ci * 0, 0, 4, 0, 0, | X 2498c2ecf20Sopenharmony_ci * 0, 0 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * buffer: 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4 2528c2ecf20Sopenharmony_ci * | | | | | | | | | | | | | | | 2538c2ecf20Sopenharmony_ci * 0, 0, 4, 0, | | | | | | | | | | | 2548c2ecf20Sopenharmony_ci * 0, 0, 4, 0, | | | | | | | 2558c2ecf20Sopenharmony_ci * 0, 0, 4, 0, | | | 2568c2ecf20Sopenharmony_ci * 0 0 4 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * Pattern is found 3 times, the remaining is 1 which results from 2598c2ecf20Sopenharmony_ci * (max_period * 3) % suffix_period. This value is the index in the 2608c2ecf20Sopenharmony_ci * suffix arrays. The suffix array for a period 4 has the value 4 2618c2ecf20Sopenharmony_ci * at index 1. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci#define EMA_ALPHA_VAL 64 2648c2ecf20Sopenharmony_ci#define EMA_ALPHA_SHIFT 7 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci#define PREDICTION_PERIOD_MIN 3 2678c2ecf20Sopenharmony_ci#define PREDICTION_PERIOD_MAX 5 2688c2ecf20Sopenharmony_ci#define PREDICTION_FACTOR 4 2698c2ecf20Sopenharmony_ci#define PREDICTION_MAX 10 /* 2 ^ PREDICTION_MAX useconds */ 2708c2ecf20Sopenharmony_ci#define PREDICTION_BUFFER_SIZE 16 /* slots for EMAs, hardly more than 16 */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * Number of elements in the circular buffer: If it happens it was 2748c2ecf20Sopenharmony_ci * flushed before, then the number of elements could be smaller than 2758c2ecf20Sopenharmony_ci * IRQ_TIMINGS_SIZE, so the count is used, otherwise the array size is 2768c2ecf20Sopenharmony_ci * used as we wrapped. The index begins from zero when we did not 2778c2ecf20Sopenharmony_ci * wrap. That could be done in a nicer way with the proper circular 2788c2ecf20Sopenharmony_ci * array structure type but with the cost of extra computation in the 2798c2ecf20Sopenharmony_ci * interrupt handler hot path. We choose efficiency. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci#define for_each_irqts(i, irqts) \ 2828c2ecf20Sopenharmony_ci for (i = irqts->count < IRQ_TIMINGS_SIZE ? \ 2838c2ecf20Sopenharmony_ci 0 : irqts->count & IRQ_TIMINGS_MASK, \ 2848c2ecf20Sopenharmony_ci irqts->count = min(IRQ_TIMINGS_SIZE, \ 2858c2ecf20Sopenharmony_ci irqts->count); \ 2868c2ecf20Sopenharmony_ci irqts->count > 0; irqts->count--, \ 2878c2ecf20Sopenharmony_ci i = (i + 1) & IRQ_TIMINGS_MASK) 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistruct irqt_stat { 2908c2ecf20Sopenharmony_ci u64 last_ts; 2918c2ecf20Sopenharmony_ci u64 ema_time[PREDICTION_BUFFER_SIZE]; 2928c2ecf20Sopenharmony_ci int timings[IRQ_TIMINGS_SIZE]; 2938c2ecf20Sopenharmony_ci int circ_timings[IRQ_TIMINGS_SIZE]; 2948c2ecf20Sopenharmony_ci int count; 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * Exponential moving average computation 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic u64 irq_timings_ema_new(u64 value, u64 ema_old) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci s64 diff; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (unlikely(!ema_old)) 3058c2ecf20Sopenharmony_ci return value; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci diff = (value - ema_old) * EMA_ALPHA_VAL; 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * We can use a s64 type variable to be added with the u64 3108c2ecf20Sopenharmony_ci * ema_old variable as this one will never have its topmost 3118c2ecf20Sopenharmony_ci * bit set, it will be always smaller than 2^63 nanosec 3128c2ecf20Sopenharmony_ci * interrupt interval (292 years). 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci return ema_old + (diff >> EMA_ALPHA_SHIFT); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int irq_timings_next_event_index(int *buffer, size_t len, int period_max) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci int period; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * Move the beginning pointer to the end minus the max period x 3. 3238c2ecf20Sopenharmony_ci * We are at the point we can begin searching the pattern 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci buffer = &buffer[len - (period_max * 3)]; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Adjust the length to the maximum allowed period x 3 */ 3288c2ecf20Sopenharmony_ci len = period_max * 3; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * The buffer contains the suite of intervals, in a ilog2 3328c2ecf20Sopenharmony_ci * basis, we are looking for a repetition. We point the 3338c2ecf20Sopenharmony_ci * beginning of the search three times the length of the 3348c2ecf20Sopenharmony_ci * period beginning at the end of the buffer. We do that for 3358c2ecf20Sopenharmony_ci * each suffix. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci for (period = period_max; period >= PREDICTION_PERIOD_MIN; period--) { 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * The first comparison always succeed because the 3418c2ecf20Sopenharmony_ci * suffix is deduced from the first n-period bytes of 3428c2ecf20Sopenharmony_ci * the buffer and we compare the initial suffix with 3438c2ecf20Sopenharmony_ci * itself, so we can skip the first iteration. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci int idx = period; 3468c2ecf20Sopenharmony_ci size_t size = period; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* 3498c2ecf20Sopenharmony_ci * We look if the suite with period 'i' repeat 3508c2ecf20Sopenharmony_ci * itself. If it is truncated at the end, as it 3518c2ecf20Sopenharmony_ci * repeats we can use the period to find out the next 3528c2ecf20Sopenharmony_ci * element with the modulo. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci while (!memcmp(buffer, &buffer[idx], size * sizeof(int))) { 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* 3578c2ecf20Sopenharmony_ci * Move the index in a period basis 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci idx += size; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * If this condition is reached, all previous 3638c2ecf20Sopenharmony_ci * memcmp were successful, so the period is 3648c2ecf20Sopenharmony_ci * found. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci if (idx == len) 3678c2ecf20Sopenharmony_ci return buffer[len % period]; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * If the remaining elements to compare are 3718c2ecf20Sopenharmony_ci * smaller than the period, readjust the size 3728c2ecf20Sopenharmony_ci * of the comparison for the last iteration. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci if (len - idx < period) 3758c2ecf20Sopenharmony_ci size = len - idx; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return -1; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic u64 __irq_timings_next_event(struct irqt_stat *irqs, int irq, u64 now) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int index, i, period_max, count, start, min = INT_MAX; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if ((now - irqs->last_ts) >= NSEC_PER_SEC) { 3878c2ecf20Sopenharmony_ci irqs->count = irqs->last_ts = 0; 3888c2ecf20Sopenharmony_ci return U64_MAX; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * As we want to find three times the repetition, we need a 3938c2ecf20Sopenharmony_ci * number of intervals greater or equal to three times the 3948c2ecf20Sopenharmony_ci * maximum period, otherwise we truncate the max period. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci period_max = irqs->count > (3 * PREDICTION_PERIOD_MAX) ? 3978c2ecf20Sopenharmony_ci PREDICTION_PERIOD_MAX : irqs->count / 3; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* 4008c2ecf20Sopenharmony_ci * If we don't have enough irq timings for this prediction, 4018c2ecf20Sopenharmony_ci * just bail out. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci if (period_max <= PREDICTION_PERIOD_MIN) 4048c2ecf20Sopenharmony_ci return U64_MAX; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* 4078c2ecf20Sopenharmony_ci * 'count' will depends if the circular buffer wrapped or not 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci count = irqs->count < IRQ_TIMINGS_SIZE ? 4108c2ecf20Sopenharmony_ci irqs->count : IRQ_TIMINGS_SIZE; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci start = irqs->count < IRQ_TIMINGS_SIZE ? 4138c2ecf20Sopenharmony_ci 0 : (irqs->count & IRQ_TIMINGS_MASK); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * Copy the content of the circular buffer into another buffer 4178c2ecf20Sopenharmony_ci * in order to linearize the buffer instead of dealing with 4188c2ecf20Sopenharmony_ci * wrapping indexes and shifted array which will be prone to 4198c2ecf20Sopenharmony_ci * error and extremelly difficult to debug. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 4228c2ecf20Sopenharmony_ci int index = (start + i) & IRQ_TIMINGS_MASK; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci irqs->timings[i] = irqs->circ_timings[index]; 4258c2ecf20Sopenharmony_ci min = min_t(int, irqs->timings[i], min); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci index = irq_timings_next_event_index(irqs->timings, count, period_max); 4298c2ecf20Sopenharmony_ci if (index < 0) 4308c2ecf20Sopenharmony_ci return irqs->last_ts + irqs->ema_time[min]; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return irqs->last_ts + irqs->ema_time[index]; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic __always_inline int irq_timings_interval_index(u64 interval) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * The PREDICTION_FACTOR increase the interval size for the 4398c2ecf20Sopenharmony_ci * array of exponential average. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci u64 interval_us = (interval >> 10) / PREDICTION_FACTOR; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return likely(interval_us) ? ilog2(interval_us) : 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic __always_inline void __irq_timings_store(int irq, struct irqt_stat *irqs, 4478c2ecf20Sopenharmony_ci u64 interval) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci int index; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * Get the index in the ema table for this interrupt. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci index = irq_timings_interval_index(interval); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (index > PREDICTION_BUFFER_SIZE - 1) { 4578c2ecf20Sopenharmony_ci irqs->count = 0; 4588c2ecf20Sopenharmony_ci return; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* 4628c2ecf20Sopenharmony_ci * Store the index as an element of the pattern in another 4638c2ecf20Sopenharmony_ci * circular array. 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci irqs->circ_timings[irqs->count & IRQ_TIMINGS_MASK] = index; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci irqs->ema_time[index] = irq_timings_ema_new(interval, 4688c2ecf20Sopenharmony_ci irqs->ema_time[index]); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci irqs->count++; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci u64 old_ts = irqs->last_ts; 4768c2ecf20Sopenharmony_ci u64 interval; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * The timestamps are absolute time values, we need to compute 4808c2ecf20Sopenharmony_ci * the timing interval between two interrupts. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci irqs->last_ts = ts; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * The interval type is u64 in order to deal with the same 4868c2ecf20Sopenharmony_ci * type in our computation, that prevent mindfuck issues with 4878c2ecf20Sopenharmony_ci * overflow, sign and division. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci interval = ts - old_ts; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* 4928c2ecf20Sopenharmony_ci * The interrupt triggered more than one second apart, that 4938c2ecf20Sopenharmony_ci * ends the sequence as predictable for our purpose. In this 4948c2ecf20Sopenharmony_ci * case, assume we have the beginning of a sequence and the 4958c2ecf20Sopenharmony_ci * timestamp is the first value. As it is impossible to 4968c2ecf20Sopenharmony_ci * predict anything at this point, return. 4978c2ecf20Sopenharmony_ci * 4988c2ecf20Sopenharmony_ci * Note the first timestamp of the sequence will always fall 4998c2ecf20Sopenharmony_ci * in this test because the old_ts is zero. That is what we 5008c2ecf20Sopenharmony_ci * want as we need another timestamp to compute an interval. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci if (interval >= NSEC_PER_SEC) { 5038c2ecf20Sopenharmony_ci irqs->count = 0; 5048c2ecf20Sopenharmony_ci return; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci __irq_timings_store(irq, irqs, interval); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/** 5118c2ecf20Sopenharmony_ci * irq_timings_next_event - Return when the next event is supposed to arrive 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * During the last busy cycle, the number of interrupts is incremented 5148c2ecf20Sopenharmony_ci * and stored in the irq_timings structure. This information is 5158c2ecf20Sopenharmony_ci * necessary to: 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * - know if the index in the table wrapped up: 5188c2ecf20Sopenharmony_ci * 5198c2ecf20Sopenharmony_ci * If more than the array size interrupts happened during the 5208c2ecf20Sopenharmony_ci * last busy/idle cycle, the index wrapped up and we have to 5218c2ecf20Sopenharmony_ci * begin with the next element in the array which is the last one 5228c2ecf20Sopenharmony_ci * in the sequence, otherwise it is a the index 0. 5238c2ecf20Sopenharmony_ci * 5248c2ecf20Sopenharmony_ci * - have an indication of the interrupts activity on this CPU 5258c2ecf20Sopenharmony_ci * (eg. irq/sec) 5268c2ecf20Sopenharmony_ci * 5278c2ecf20Sopenharmony_ci * The values are 'consumed' after inserting in the statistical model, 5288c2ecf20Sopenharmony_ci * thus the count is reinitialized. 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * The array of values **must** be browsed in the time direction, the 5318c2ecf20Sopenharmony_ci * timestamp must increase between an element and the next one. 5328c2ecf20Sopenharmony_ci * 5338c2ecf20Sopenharmony_ci * Returns a nanosec time based estimation of the earliest interrupt, 5348c2ecf20Sopenharmony_ci * U64_MAX otherwise. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ciu64 irq_timings_next_event(u64 now) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct irq_timings *irqts = this_cpu_ptr(&irq_timings); 5398c2ecf20Sopenharmony_ci struct irqt_stat *irqs; 5408c2ecf20Sopenharmony_ci struct irqt_stat __percpu *s; 5418c2ecf20Sopenharmony_ci u64 ts, next_evt = U64_MAX; 5428c2ecf20Sopenharmony_ci int i, irq = 0; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* 5458c2ecf20Sopenharmony_ci * This function must be called with the local irq disabled in 5468c2ecf20Sopenharmony_ci * order to prevent the timings circular buffer to be updated 5478c2ecf20Sopenharmony_ci * while we are reading it. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci lockdep_assert_irqs_disabled(); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (!irqts->count) 5528c2ecf20Sopenharmony_ci return next_evt; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* 5558c2ecf20Sopenharmony_ci * Number of elements in the circular buffer: If it happens it 5568c2ecf20Sopenharmony_ci * was flushed before, then the number of elements could be 5578c2ecf20Sopenharmony_ci * smaller than IRQ_TIMINGS_SIZE, so the count is used, 5588c2ecf20Sopenharmony_ci * otherwise the array size is used as we wrapped. The index 5598c2ecf20Sopenharmony_ci * begins from zero when we did not wrap. That could be done 5608c2ecf20Sopenharmony_ci * in a nicer way with the proper circular array structure 5618c2ecf20Sopenharmony_ci * type but with the cost of extra computation in the 5628c2ecf20Sopenharmony_ci * interrupt handler hot path. We choose efficiency. 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * Inject measured irq/timestamp to the pattern prediction 5658c2ecf20Sopenharmony_ci * model while decrementing the counter because we consume the 5668c2ecf20Sopenharmony_ci * data from our circular buffer. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci for_each_irqts(i, irqts) { 5698c2ecf20Sopenharmony_ci irq = irq_timing_decode(irqts->values[i], &ts); 5708c2ecf20Sopenharmony_ci s = idr_find(&irqt_stats, irq); 5718c2ecf20Sopenharmony_ci if (s) 5728c2ecf20Sopenharmony_ci irq_timings_store(irq, this_cpu_ptr(s), ts); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* 5768c2ecf20Sopenharmony_ci * Look in the list of interrupts' statistics, the earliest 5778c2ecf20Sopenharmony_ci * next event. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci idr_for_each_entry(&irqt_stats, s, i) { 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci irqs = this_cpu_ptr(s); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ts = __irq_timings_next_event(irqs, i, now); 5848c2ecf20Sopenharmony_ci if (ts <= now) 5858c2ecf20Sopenharmony_ci return now; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (ts < next_evt) 5888c2ecf20Sopenharmony_ci next_evt = ts; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return next_evt; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_civoid irq_timings_free(int irq) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct irqt_stat __percpu *s; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci s = idr_find(&irqt_stats, irq); 5998c2ecf20Sopenharmony_ci if (s) { 6008c2ecf20Sopenharmony_ci free_percpu(s); 6018c2ecf20Sopenharmony_ci idr_remove(&irqt_stats, irq); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ciint irq_timings_alloc(int irq) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct irqt_stat __percpu *s; 6088c2ecf20Sopenharmony_ci int id; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * Some platforms can have the same private interrupt per cpu, 6128c2ecf20Sopenharmony_ci * so this function may be called several times with the 6138c2ecf20Sopenharmony_ci * same interrupt number. Just bail out in case the per cpu 6148c2ecf20Sopenharmony_ci * stat structure is already allocated. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci s = idr_find(&irqt_stats, irq); 6178c2ecf20Sopenharmony_ci if (s) 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci s = alloc_percpu(*s); 6218c2ecf20Sopenharmony_ci if (!s) 6228c2ecf20Sopenharmony_ci return -ENOMEM; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci idr_preload(GFP_KERNEL); 6258c2ecf20Sopenharmony_ci id = idr_alloc(&irqt_stats, s, irq, irq + 1, GFP_NOWAIT); 6268c2ecf20Sopenharmony_ci idr_preload_end(); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (id < 0) { 6298c2ecf20Sopenharmony_ci free_percpu(s); 6308c2ecf20Sopenharmony_ci return id; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return 0; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci#ifdef CONFIG_TEST_IRQ_TIMINGS 6378c2ecf20Sopenharmony_cistruct timings_intervals { 6388c2ecf20Sopenharmony_ci u64 *intervals; 6398c2ecf20Sopenharmony_ci size_t count; 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* 6438c2ecf20Sopenharmony_ci * Intervals are given in nanosecond base 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_cistatic u64 intervals0[] __initdata = { 6468c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6478c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6488c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6498c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6508c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6518c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6528c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6538c2ecf20Sopenharmony_ci 10000, 50000, 200000, 500000, 6548c2ecf20Sopenharmony_ci 10000, 50000, 200000, 6558c2ecf20Sopenharmony_ci}; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic u64 intervals1[] __initdata = { 6588c2ecf20Sopenharmony_ci 223947000, 1240000, 1384000, 1386000, 1386000, 6598c2ecf20Sopenharmony_ci 217416000, 1236000, 1384000, 1386000, 1387000, 6608c2ecf20Sopenharmony_ci 214719000, 1241000, 1386000, 1387000, 1384000, 6618c2ecf20Sopenharmony_ci 213696000, 1234000, 1384000, 1386000, 1388000, 6628c2ecf20Sopenharmony_ci 219904000, 1240000, 1385000, 1389000, 1385000, 6638c2ecf20Sopenharmony_ci 212240000, 1240000, 1386000, 1386000, 1386000, 6648c2ecf20Sopenharmony_ci 214415000, 1236000, 1384000, 1386000, 1387000, 6658c2ecf20Sopenharmony_ci 214276000, 1234000, 6668c2ecf20Sopenharmony_ci}; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic u64 intervals2[] __initdata = { 6698c2ecf20Sopenharmony_ci 4000, 3000, 5000, 100000, 6708c2ecf20Sopenharmony_ci 3000, 3000, 5000, 117000, 6718c2ecf20Sopenharmony_ci 4000, 4000, 5000, 112000, 6728c2ecf20Sopenharmony_ci 4000, 3000, 4000, 110000, 6738c2ecf20Sopenharmony_ci 3000, 5000, 3000, 117000, 6748c2ecf20Sopenharmony_ci 4000, 4000, 5000, 112000, 6758c2ecf20Sopenharmony_ci 4000, 3000, 4000, 110000, 6768c2ecf20Sopenharmony_ci 3000, 4000, 5000, 112000, 6778c2ecf20Sopenharmony_ci 4000, 6788c2ecf20Sopenharmony_ci}; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic u64 intervals3[] __initdata = { 6818c2ecf20Sopenharmony_ci 1385000, 212240000, 1240000, 6828c2ecf20Sopenharmony_ci 1386000, 214415000, 1236000, 6838c2ecf20Sopenharmony_ci 1384000, 214276000, 1234000, 6848c2ecf20Sopenharmony_ci 1386000, 214415000, 1236000, 6858c2ecf20Sopenharmony_ci 1385000, 212240000, 1240000, 6868c2ecf20Sopenharmony_ci 1386000, 214415000, 1236000, 6878c2ecf20Sopenharmony_ci 1384000, 214276000, 1234000, 6888c2ecf20Sopenharmony_ci 1386000, 214415000, 1236000, 6898c2ecf20Sopenharmony_ci 1385000, 212240000, 1240000, 6908c2ecf20Sopenharmony_ci}; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic u64 intervals4[] __initdata = { 6938c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 6948c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 6958c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 6968c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 6978c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 6988c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 6998c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 7008c2ecf20Sopenharmony_ci 10000, 50000, 10000, 50000, 7018c2ecf20Sopenharmony_ci 10000, 7028c2ecf20Sopenharmony_ci}; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic struct timings_intervals tis[] __initdata = { 7058c2ecf20Sopenharmony_ci { intervals0, ARRAY_SIZE(intervals0) }, 7068c2ecf20Sopenharmony_ci { intervals1, ARRAY_SIZE(intervals1) }, 7078c2ecf20Sopenharmony_ci { intervals2, ARRAY_SIZE(intervals2) }, 7088c2ecf20Sopenharmony_ci { intervals3, ARRAY_SIZE(intervals3) }, 7098c2ecf20Sopenharmony_ci { intervals4, ARRAY_SIZE(intervals4) }, 7108c2ecf20Sopenharmony_ci}; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int __init irq_timings_test_next_index(struct timings_intervals *ti) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci int _buffer[IRQ_TIMINGS_SIZE]; 7158c2ecf20Sopenharmony_ci int buffer[IRQ_TIMINGS_SIZE]; 7168c2ecf20Sopenharmony_ci int index, start, i, count, period_max; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci count = ti->count - 1; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci period_max = count > (3 * PREDICTION_PERIOD_MAX) ? 7218c2ecf20Sopenharmony_ci PREDICTION_PERIOD_MAX : count / 3; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* 7248c2ecf20Sopenharmony_ci * Inject all values except the last one which will be used 7258c2ecf20Sopenharmony_ci * to compare with the next index result. 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ci pr_debug("index suite: "); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 7308c2ecf20Sopenharmony_ci index = irq_timings_interval_index(ti->intervals[i]); 7318c2ecf20Sopenharmony_ci _buffer[i & IRQ_TIMINGS_MASK] = index; 7328c2ecf20Sopenharmony_ci pr_cont("%d ", index); 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci start = count < IRQ_TIMINGS_SIZE ? 0 : 7368c2ecf20Sopenharmony_ci count & IRQ_TIMINGS_MASK; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci count = min_t(int, count, IRQ_TIMINGS_SIZE); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 7418c2ecf20Sopenharmony_ci int index = (start + i) & IRQ_TIMINGS_MASK; 7428c2ecf20Sopenharmony_ci buffer[i] = _buffer[index]; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci index = irq_timings_next_event_index(buffer, count, period_max); 7468c2ecf20Sopenharmony_ci i = irq_timings_interval_index(ti->intervals[ti->count - 1]); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci if (index != i) { 7498c2ecf20Sopenharmony_ci pr_err("Expected (%d) and computed (%d) next indexes differ\n", 7508c2ecf20Sopenharmony_ci i, index); 7518c2ecf20Sopenharmony_ci return -EINVAL; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic int __init irq_timings_next_index_selftest(void) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci int i, ret; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tis); i++) { 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci pr_info("---> Injecting intervals number #%d (count=%zd)\n", 7648c2ecf20Sopenharmony_ci i, tis[i].count); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci ret = irq_timings_test_next_index(&tis[i]); 7678c2ecf20Sopenharmony_ci if (ret) 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci return ret; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic int __init irq_timings_test_irqs(struct timings_intervals *ti) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct irqt_stat __percpu *s; 7778c2ecf20Sopenharmony_ci struct irqt_stat *irqs; 7788c2ecf20Sopenharmony_ci int i, index, ret, irq = 0xACE5; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci ret = irq_timings_alloc(irq); 7818c2ecf20Sopenharmony_ci if (ret) { 7828c2ecf20Sopenharmony_ci pr_err("Failed to allocate irq timings\n"); 7838c2ecf20Sopenharmony_ci return ret; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci s = idr_find(&irqt_stats, irq); 7878c2ecf20Sopenharmony_ci if (!s) { 7888c2ecf20Sopenharmony_ci ret = -EIDRM; 7898c2ecf20Sopenharmony_ci goto out; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci irqs = this_cpu_ptr(s); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci for (i = 0; i < ti->count; i++) { 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci index = irq_timings_interval_index(ti->intervals[i]); 7978c2ecf20Sopenharmony_ci pr_debug("%d: interval=%llu ema_index=%d\n", 7988c2ecf20Sopenharmony_ci i, ti->intervals[i], index); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci __irq_timings_store(irq, irqs, ti->intervals[i]); 8018c2ecf20Sopenharmony_ci if (irqs->circ_timings[i & IRQ_TIMINGS_MASK] != index) { 8028c2ecf20Sopenharmony_ci ret = -EBADSLT; 8038c2ecf20Sopenharmony_ci pr_err("Failed to store in the circular buffer\n"); 8048c2ecf20Sopenharmony_ci goto out; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (irqs->count != ti->count) { 8098c2ecf20Sopenharmony_ci ret = -ERANGE; 8108c2ecf20Sopenharmony_ci pr_err("Count differs\n"); 8118c2ecf20Sopenharmony_ci goto out; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci ret = 0; 8158c2ecf20Sopenharmony_ciout: 8168c2ecf20Sopenharmony_ci irq_timings_free(irq); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return ret; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int __init irq_timings_irqs_selftest(void) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci int i, ret; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tis); i++) { 8268c2ecf20Sopenharmony_ci pr_info("---> Injecting intervals number #%d (count=%zd)\n", 8278c2ecf20Sopenharmony_ci i, tis[i].count); 8288c2ecf20Sopenharmony_ci ret = irq_timings_test_irqs(&tis[i]); 8298c2ecf20Sopenharmony_ci if (ret) 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return ret; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic int __init irq_timings_test_irqts(struct irq_timings *irqts, 8378c2ecf20Sopenharmony_ci unsigned count) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci int start = count >= IRQ_TIMINGS_SIZE ? count - IRQ_TIMINGS_SIZE : 0; 8408c2ecf20Sopenharmony_ci int i, irq, oirq = 0xBEEF; 8418c2ecf20Sopenharmony_ci u64 ots = 0xDEAD, ts; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* 8448c2ecf20Sopenharmony_ci * Fill the circular buffer by using the dedicated function. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 8478c2ecf20Sopenharmony_ci pr_debug("%d: index=%d, ts=%llX irq=%X\n", 8488c2ecf20Sopenharmony_ci i, i & IRQ_TIMINGS_MASK, ots + i, oirq + i); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci irq_timings_push(ots + i, oirq + i); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* 8548c2ecf20Sopenharmony_ci * Compute the first elements values after the index wrapped 8558c2ecf20Sopenharmony_ci * up or not. 8568c2ecf20Sopenharmony_ci */ 8578c2ecf20Sopenharmony_ci ots += start; 8588c2ecf20Sopenharmony_ci oirq += start; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* 8618c2ecf20Sopenharmony_ci * Test the circular buffer count is correct. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ci pr_debug("---> Checking timings array count (%d) is right\n", count); 8648c2ecf20Sopenharmony_ci if (WARN_ON(irqts->count != count)) 8658c2ecf20Sopenharmony_ci return -EINVAL; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* 8688c2ecf20Sopenharmony_ci * Test the macro allowing to browse all the irqts. 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_ci pr_debug("---> Checking the for_each_irqts() macro\n"); 8718c2ecf20Sopenharmony_ci for_each_irqts(i, irqts) { 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci irq = irq_timing_decode(irqts->values[i], &ts); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci pr_debug("index=%d, ts=%llX / %llX, irq=%X / %X\n", 8768c2ecf20Sopenharmony_ci i, ts, ots, irq, oirq); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (WARN_ON(ts != ots || irq != oirq)) 8798c2ecf20Sopenharmony_ci return -EINVAL; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci ots++; oirq++; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* 8858c2ecf20Sopenharmony_ci * The circular buffer should have be flushed when browsed 8868c2ecf20Sopenharmony_ci * with for_each_irqts 8878c2ecf20Sopenharmony_ci */ 8888c2ecf20Sopenharmony_ci pr_debug("---> Checking timings array is empty after browsing it\n"); 8898c2ecf20Sopenharmony_ci if (WARN_ON(irqts->count)) 8908c2ecf20Sopenharmony_ci return -EINVAL; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return 0; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic int __init irq_timings_irqts_selftest(void) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct irq_timings *irqts = this_cpu_ptr(&irq_timings); 8988c2ecf20Sopenharmony_ci int i, ret; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* 9018c2ecf20Sopenharmony_ci * Test the circular buffer with different number of 9028c2ecf20Sopenharmony_ci * elements. The purpose is to test at the limits (empty, half 9038c2ecf20Sopenharmony_ci * full, full, wrapped with the cursor at the boundaries, 9048c2ecf20Sopenharmony_ci * wrapped several times, etc ... 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_ci int count[] = { 0, 9078c2ecf20Sopenharmony_ci IRQ_TIMINGS_SIZE >> 1, 9088c2ecf20Sopenharmony_ci IRQ_TIMINGS_SIZE, 9098c2ecf20Sopenharmony_ci IRQ_TIMINGS_SIZE + (IRQ_TIMINGS_SIZE >> 1), 9108c2ecf20Sopenharmony_ci 2 * IRQ_TIMINGS_SIZE, 9118c2ecf20Sopenharmony_ci (2 * IRQ_TIMINGS_SIZE) + 3, 9128c2ecf20Sopenharmony_ci }; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(count); i++) { 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci pr_info("---> Checking the timings with %d/%d values\n", 9178c2ecf20Sopenharmony_ci count[i], IRQ_TIMINGS_SIZE); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = irq_timings_test_irqts(irqts, count[i]); 9208c2ecf20Sopenharmony_ci if (ret) 9218c2ecf20Sopenharmony_ci break; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return ret; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic int __init irq_timings_selftest(void) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci int ret; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci pr_info("------------------- selftest start -----------------\n"); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci /* 9348c2ecf20Sopenharmony_ci * At this point, we don't except any subsystem to use the irq 9358c2ecf20Sopenharmony_ci * timings but us, so it should not be enabled. 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_ci if (static_branch_unlikely(&irq_timing_enabled)) { 9388c2ecf20Sopenharmony_ci pr_warn("irq timings already initialized, skipping selftest\n"); 9398c2ecf20Sopenharmony_ci return 0; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci ret = irq_timings_irqts_selftest(); 9438c2ecf20Sopenharmony_ci if (ret) 9448c2ecf20Sopenharmony_ci goto out; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ret = irq_timings_irqs_selftest(); 9478c2ecf20Sopenharmony_ci if (ret) 9488c2ecf20Sopenharmony_ci goto out; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci ret = irq_timings_next_index_selftest(); 9518c2ecf20Sopenharmony_ciout: 9528c2ecf20Sopenharmony_ci pr_info("---------- selftest end with %s -----------\n", 9538c2ecf20Sopenharmony_ci ret ? "failure" : "success"); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return ret; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ciearly_initcall(irq_timings_selftest); 9588c2ecf20Sopenharmony_ci#endif 959