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