1*c942fddfSThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-or-later */
245e898b7SWaiman Long /*
345e898b7SWaiman Long *
4fb346fd9SWaiman Long * Authors: Waiman Long <[email protected]>
545e898b7SWaiman Long */
645e898b7SWaiman Long
7ad53fa10SWaiman Long #include "lock_events.h"
845e898b7SWaiman Long
9fb346fd9SWaiman Long #ifdef CONFIG_LOCK_EVENT_COUNTS
10fb346fd9SWaiman Long #ifdef CONFIG_PARAVIRT_SPINLOCKS
1145e898b7SWaiman Long /*
12fb346fd9SWaiman Long * Collect pvqspinlock locking event counts
1345e898b7SWaiman Long */
1445e898b7SWaiman Long #include <linux/sched.h>
15e6017571SIngo Molnar #include <linux/sched/clock.h>
1645e898b7SWaiman Long #include <linux/fs.h>
1745e898b7SWaiman Long
18ad53fa10SWaiman Long #define EVENT_COUNT(ev) lockevents[LOCKEVENT_ ## ev]
19ad53fa10SWaiman Long
2045e898b7SWaiman Long /*
21fb346fd9SWaiman Long * PV specific per-cpu counter
2245e898b7SWaiman Long */
2345e898b7SWaiman Long static DEFINE_PER_CPU(u64, pv_kick_time);
2445e898b7SWaiman Long
2545e898b7SWaiman Long /*
26fb346fd9SWaiman Long * Function to read and return the PV qspinlock counts.
2745e898b7SWaiman Long *
2845e898b7SWaiman Long * The following counters are handled specially:
29ad53fa10SWaiman Long * 1. pv_latency_kick
3045e898b7SWaiman Long * Average kick latency (ns) = pv_latency_kick/pv_kick_unlock
31ad53fa10SWaiman Long * 2. pv_latency_wake
3245e898b7SWaiman Long * Average wake latency (ns) = pv_latency_wake/pv_kick_wake
33ad53fa10SWaiman Long * 3. pv_hash_hops
3445e898b7SWaiman Long * Average hops/hash = pv_hash_hops/pv_kick_unlock
3545e898b7SWaiman Long */
lockevent_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)36fb346fd9SWaiman Long ssize_t lockevent_read(struct file *file, char __user *user_buf,
3745e898b7SWaiman Long size_t count, loff_t *ppos)
3845e898b7SWaiman Long {
3945e898b7SWaiman Long char buf[64];
40ad53fa10SWaiman Long int cpu, id, len;
41ad53fa10SWaiman Long u64 sum = 0, kicks = 0;
4245e898b7SWaiman Long
4345e898b7SWaiman Long /*
4445e898b7SWaiman Long * Get the counter ID stored in file->f_inode->i_private
4545e898b7SWaiman Long */
46ad53fa10SWaiman Long id = (long)file_inode(file)->i_private;
4745e898b7SWaiman Long
48ad53fa10SWaiman Long if (id >= lockevent_num)
4945e898b7SWaiman Long return -EBADF;
5045e898b7SWaiman Long
5145e898b7SWaiman Long for_each_possible_cpu(cpu) {
52ad53fa10SWaiman Long sum += per_cpu(lockevents[id], cpu);
5345e898b7SWaiman Long /*
54ad53fa10SWaiman Long * Need to sum additional counters for some of them
5545e898b7SWaiman Long */
56ad53fa10SWaiman Long switch (id) {
5745e898b7SWaiman Long
58ad53fa10SWaiman Long case LOCKEVENT_pv_latency_kick:
59ad53fa10SWaiman Long case LOCKEVENT_pv_hash_hops:
60ad53fa10SWaiman Long kicks += per_cpu(EVENT_COUNT(pv_kick_unlock), cpu);
6145e898b7SWaiman Long break;
6245e898b7SWaiman Long
63ad53fa10SWaiman Long case LOCKEVENT_pv_latency_wake:
64ad53fa10SWaiman Long kicks += per_cpu(EVENT_COUNT(pv_kick_wake), cpu);
6545e898b7SWaiman Long break;
6645e898b7SWaiman Long }
6745e898b7SWaiman Long }
6845e898b7SWaiman Long
69ad53fa10SWaiman Long if (id == LOCKEVENT_pv_hash_hops) {
7066876595SDavidlohr Bueso u64 frac = 0;
7145e898b7SWaiman Long
7266876595SDavidlohr Bueso if (kicks) {
73ad53fa10SWaiman Long frac = 100ULL * do_div(sum, kicks);
7445e898b7SWaiman Long frac = DIV_ROUND_CLOSEST_ULL(frac, kicks);
7566876595SDavidlohr Bueso }
7645e898b7SWaiman Long
7745e898b7SWaiman Long /*
7845e898b7SWaiman Long * Return a X.XX decimal number
7945e898b7SWaiman Long */
80ad53fa10SWaiman Long len = snprintf(buf, sizeof(buf) - 1, "%llu.%02llu\n",
81ad53fa10SWaiman Long sum, frac);
8245e898b7SWaiman Long } else {
8345e898b7SWaiman Long /*
8445e898b7SWaiman Long * Round to the nearest ns
8545e898b7SWaiman Long */
86ad53fa10SWaiman Long if ((id == LOCKEVENT_pv_latency_kick) ||
87ad53fa10SWaiman Long (id == LOCKEVENT_pv_latency_wake)) {
8845e898b7SWaiman Long if (kicks)
89ad53fa10SWaiman Long sum = DIV_ROUND_CLOSEST_ULL(sum, kicks);
9045e898b7SWaiman Long }
91ad53fa10SWaiman Long len = snprintf(buf, sizeof(buf) - 1, "%llu\n", sum);
9245e898b7SWaiman Long }
9345e898b7SWaiman Long
9445e898b7SWaiman Long return simple_read_from_buffer(user_buf, count, ppos, buf, len);
9545e898b7SWaiman Long }
9645e898b7SWaiman Long
9745e898b7SWaiman Long /*
9845e898b7SWaiman Long * PV hash hop count
9945e898b7SWaiman Long */
lockevent_pv_hop(int hopcnt)100ad53fa10SWaiman Long static inline void lockevent_pv_hop(int hopcnt)
10145e898b7SWaiman Long {
102ad53fa10SWaiman Long this_cpu_add(EVENT_COUNT(pv_hash_hops), hopcnt);
10345e898b7SWaiman Long }
10445e898b7SWaiman Long
10545e898b7SWaiman Long /*
10645e898b7SWaiman Long * Replacement function for pv_kick()
10745e898b7SWaiman Long */
__pv_kick(int cpu)10845e898b7SWaiman Long static inline void __pv_kick(int cpu)
10945e898b7SWaiman Long {
11045e898b7SWaiman Long u64 start = sched_clock();
11145e898b7SWaiman Long
11245e898b7SWaiman Long per_cpu(pv_kick_time, cpu) = start;
11345e898b7SWaiman Long pv_kick(cpu);
114ad53fa10SWaiman Long this_cpu_add(EVENT_COUNT(pv_latency_kick), sched_clock() - start);
11545e898b7SWaiman Long }
11645e898b7SWaiman Long
11745e898b7SWaiman Long /*
11845e898b7SWaiman Long * Replacement function for pv_wait()
11945e898b7SWaiman Long */
__pv_wait(u8 * ptr,u8 val)12045e898b7SWaiman Long static inline void __pv_wait(u8 *ptr, u8 val)
12145e898b7SWaiman Long {
12245e898b7SWaiman Long u64 *pkick_time = this_cpu_ptr(&pv_kick_time);
12345e898b7SWaiman Long
12445e898b7SWaiman Long *pkick_time = 0;
12545e898b7SWaiman Long pv_wait(ptr, val);
12645e898b7SWaiman Long if (*pkick_time) {
127ad53fa10SWaiman Long this_cpu_add(EVENT_COUNT(pv_latency_wake),
12845e898b7SWaiman Long sched_clock() - *pkick_time);
129ad53fa10SWaiman Long lockevent_inc(pv_kick_wake);
13045e898b7SWaiman Long }
13145e898b7SWaiman Long }
13245e898b7SWaiman Long
13345e898b7SWaiman Long #define pv_kick(c) __pv_kick(c)
13445e898b7SWaiman Long #define pv_wait(p, v) __pv_wait(p, v)
13545e898b7SWaiman Long
136fb346fd9SWaiman Long #endif /* CONFIG_PARAVIRT_SPINLOCKS */
137fb346fd9SWaiman Long
138fb346fd9SWaiman Long #else /* CONFIG_LOCK_EVENT_COUNTS */
13945e898b7SWaiman Long
lockevent_pv_hop(int hopcnt)140ad53fa10SWaiman Long static inline void lockevent_pv_hop(int hopcnt) { }
14145e898b7SWaiman Long
142fb346fd9SWaiman Long #endif /* CONFIG_LOCK_EVENT_COUNTS */
143