xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/pseries/dtl.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Virtual Processor Dispatch Trace Log
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * (C) Copyright IBM Corporation 2009
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author: Jeremy Kerr <jk@ozlabs.org>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/spinlock.h>
12*4882a593Smuzhiyun #include <asm/smp.h>
13*4882a593Smuzhiyun #include <linux/uaccess.h>
14*4882a593Smuzhiyun #include <asm/firmware.h>
15*4882a593Smuzhiyun #include <asm/dtl.h>
16*4882a593Smuzhiyun #include <asm/lppaca.h>
17*4882a593Smuzhiyun #include <asm/debugfs.h>
18*4882a593Smuzhiyun #include <asm/plpar_wrappers.h>
19*4882a593Smuzhiyun #include <asm/machdep.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun struct dtl {
22*4882a593Smuzhiyun 	struct dtl_entry	*buf;
23*4882a593Smuzhiyun 	int			cpu;
24*4882a593Smuzhiyun 	int			buf_entries;
25*4882a593Smuzhiyun 	u64			last_idx;
26*4882a593Smuzhiyun 	spinlock_t		lock;
27*4882a593Smuzhiyun };
28*4882a593Smuzhiyun static DEFINE_PER_CPU(struct dtl, cpu_dtl);
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun static u8 dtl_event_mask = DTL_LOG_ALL;
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /*
34*4882a593Smuzhiyun  * Size of per-cpu log buffers. Firmware requires that the buffer does
35*4882a593Smuzhiyun  * not cross a 4k boundary.
36*4882a593Smuzhiyun  */
37*4882a593Smuzhiyun static int dtl_buf_entries = N_DISPATCH_LOG;
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
40*4882a593Smuzhiyun struct dtl_ring {
41*4882a593Smuzhiyun 	u64	write_index;
42*4882a593Smuzhiyun 	struct dtl_entry *write_ptr;
43*4882a593Smuzhiyun 	struct dtl_entry *buf;
44*4882a593Smuzhiyun 	struct dtl_entry *buf_end;
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun static DEFINE_PER_CPU(struct dtl_ring, dtl_rings);
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun static atomic_t dtl_count;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /*
52*4882a593Smuzhiyun  * The cpu accounting code controls the DTL ring buffer, and we get
53*4882a593Smuzhiyun  * given entries as they are processed.
54*4882a593Smuzhiyun  */
consume_dtle(struct dtl_entry * dtle,u64 index)55*4882a593Smuzhiyun static void consume_dtle(struct dtl_entry *dtle, u64 index)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	struct dtl_ring *dtlr = this_cpu_ptr(&dtl_rings);
58*4882a593Smuzhiyun 	struct dtl_entry *wp = dtlr->write_ptr;
59*4882a593Smuzhiyun 	struct lppaca *vpa = local_paca->lppaca_ptr;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	if (!wp)
62*4882a593Smuzhiyun 		return;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	*wp = *dtle;
65*4882a593Smuzhiyun 	barrier();
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	/* check for hypervisor ring buffer overflow, ignore this entry if so */
68*4882a593Smuzhiyun 	if (index + N_DISPATCH_LOG < be64_to_cpu(vpa->dtl_idx))
69*4882a593Smuzhiyun 		return;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	++wp;
72*4882a593Smuzhiyun 	if (wp == dtlr->buf_end)
73*4882a593Smuzhiyun 		wp = dtlr->buf;
74*4882a593Smuzhiyun 	dtlr->write_ptr = wp;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	/* incrementing write_index makes the new entry visible */
77*4882a593Smuzhiyun 	smp_wmb();
78*4882a593Smuzhiyun 	++dtlr->write_index;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
dtl_start(struct dtl * dtl)81*4882a593Smuzhiyun static int dtl_start(struct dtl *dtl)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun 	struct dtl_ring *dtlr = &per_cpu(dtl_rings, dtl->cpu);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	dtlr->buf = dtl->buf;
86*4882a593Smuzhiyun 	dtlr->buf_end = dtl->buf + dtl->buf_entries;
87*4882a593Smuzhiyun 	dtlr->write_index = 0;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	/* setting write_ptr enables logging into our buffer */
90*4882a593Smuzhiyun 	smp_wmb();
91*4882a593Smuzhiyun 	dtlr->write_ptr = dtl->buf;
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	/* enable event logging */
94*4882a593Smuzhiyun 	lppaca_of(dtl->cpu).dtl_enable_mask |= dtl_event_mask;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	dtl_consumer = consume_dtle;
97*4882a593Smuzhiyun 	atomic_inc(&dtl_count);
98*4882a593Smuzhiyun 	return 0;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun 
dtl_stop(struct dtl * dtl)101*4882a593Smuzhiyun static void dtl_stop(struct dtl *dtl)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	struct dtl_ring *dtlr = &per_cpu(dtl_rings, dtl->cpu);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	dtlr->write_ptr = NULL;
106*4882a593Smuzhiyun 	smp_wmb();
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	dtlr->buf = NULL;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	/* restore dtl_enable_mask */
111*4882a593Smuzhiyun 	lppaca_of(dtl->cpu).dtl_enable_mask = DTL_LOG_PREEMPT;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	if (atomic_dec_and_test(&dtl_count))
114*4882a593Smuzhiyun 		dtl_consumer = NULL;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
dtl_current_index(struct dtl * dtl)117*4882a593Smuzhiyun static u64 dtl_current_index(struct dtl *dtl)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	return per_cpu(dtl_rings, dtl->cpu).write_index;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun #else /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
123*4882a593Smuzhiyun 
dtl_start(struct dtl * dtl)124*4882a593Smuzhiyun static int dtl_start(struct dtl *dtl)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	unsigned long addr;
127*4882a593Smuzhiyun 	int ret, hwcpu;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	/* Register our dtl buffer with the hypervisor. The HV expects the
130*4882a593Smuzhiyun 	 * buffer size to be passed in the second word of the buffer */
131*4882a593Smuzhiyun 	((u32 *)dtl->buf)[1] = cpu_to_be32(DISPATCH_LOG_BYTES);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	hwcpu = get_hard_smp_processor_id(dtl->cpu);
134*4882a593Smuzhiyun 	addr = __pa(dtl->buf);
135*4882a593Smuzhiyun 	ret = register_dtl(hwcpu, addr);
136*4882a593Smuzhiyun 	if (ret) {
137*4882a593Smuzhiyun 		printk(KERN_WARNING "%s: DTL registration for cpu %d (hw %d) "
138*4882a593Smuzhiyun 		       "failed with %d\n", __func__, dtl->cpu, hwcpu, ret);
139*4882a593Smuzhiyun 		return -EIO;
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	/* set our initial buffer indices */
143*4882a593Smuzhiyun 	lppaca_of(dtl->cpu).dtl_idx = 0;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	/* ensure that our updates to the lppaca fields have occurred before
146*4882a593Smuzhiyun 	 * we actually enable the logging */
147*4882a593Smuzhiyun 	smp_wmb();
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	/* enable event logging */
150*4882a593Smuzhiyun 	lppaca_of(dtl->cpu).dtl_enable_mask = dtl_event_mask;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	return 0;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
dtl_stop(struct dtl * dtl)155*4882a593Smuzhiyun static void dtl_stop(struct dtl *dtl)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	int hwcpu = get_hard_smp_processor_id(dtl->cpu);
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	lppaca_of(dtl->cpu).dtl_enable_mask = 0x0;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	unregister_dtl(hwcpu);
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
dtl_current_index(struct dtl * dtl)164*4882a593Smuzhiyun static u64 dtl_current_index(struct dtl *dtl)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	return be64_to_cpu(lppaca_of(dtl->cpu).dtl_idx);
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun #endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
169*4882a593Smuzhiyun 
dtl_enable(struct dtl * dtl)170*4882a593Smuzhiyun static int dtl_enable(struct dtl *dtl)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	long int n_entries;
173*4882a593Smuzhiyun 	long int rc;
174*4882a593Smuzhiyun 	struct dtl_entry *buf = NULL;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	if (!dtl_cache)
177*4882a593Smuzhiyun 		return -ENOMEM;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	/* only allow one reader */
180*4882a593Smuzhiyun 	if (dtl->buf)
181*4882a593Smuzhiyun 		return -EBUSY;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	/* ensure there are no other conflicting dtl users */
184*4882a593Smuzhiyun 	if (!read_trylock(&dtl_access_lock))
185*4882a593Smuzhiyun 		return -EBUSY;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	n_entries = dtl_buf_entries;
188*4882a593Smuzhiyun 	buf = kmem_cache_alloc_node(dtl_cache, GFP_KERNEL, cpu_to_node(dtl->cpu));
189*4882a593Smuzhiyun 	if (!buf) {
190*4882a593Smuzhiyun 		printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
191*4882a593Smuzhiyun 				__func__, dtl->cpu);
192*4882a593Smuzhiyun 		read_unlock(&dtl_access_lock);
193*4882a593Smuzhiyun 		return -ENOMEM;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	spin_lock(&dtl->lock);
197*4882a593Smuzhiyun 	rc = -EBUSY;
198*4882a593Smuzhiyun 	if (!dtl->buf) {
199*4882a593Smuzhiyun 		/* store the original allocation size for use during read */
200*4882a593Smuzhiyun 		dtl->buf_entries = n_entries;
201*4882a593Smuzhiyun 		dtl->buf = buf;
202*4882a593Smuzhiyun 		dtl->last_idx = 0;
203*4882a593Smuzhiyun 		rc = dtl_start(dtl);
204*4882a593Smuzhiyun 		if (rc)
205*4882a593Smuzhiyun 			dtl->buf = NULL;
206*4882a593Smuzhiyun 	}
207*4882a593Smuzhiyun 	spin_unlock(&dtl->lock);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	if (rc) {
210*4882a593Smuzhiyun 		read_unlock(&dtl_access_lock);
211*4882a593Smuzhiyun 		kmem_cache_free(dtl_cache, buf);
212*4882a593Smuzhiyun 	}
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	return rc;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
dtl_disable(struct dtl * dtl)217*4882a593Smuzhiyun static void dtl_disable(struct dtl *dtl)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun 	spin_lock(&dtl->lock);
220*4882a593Smuzhiyun 	dtl_stop(dtl);
221*4882a593Smuzhiyun 	kmem_cache_free(dtl_cache, dtl->buf);
222*4882a593Smuzhiyun 	dtl->buf = NULL;
223*4882a593Smuzhiyun 	dtl->buf_entries = 0;
224*4882a593Smuzhiyun 	spin_unlock(&dtl->lock);
225*4882a593Smuzhiyun 	read_unlock(&dtl_access_lock);
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun /* file interface */
229*4882a593Smuzhiyun 
dtl_file_open(struct inode * inode,struct file * filp)230*4882a593Smuzhiyun static int dtl_file_open(struct inode *inode, struct file *filp)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	struct dtl *dtl = inode->i_private;
233*4882a593Smuzhiyun 	int rc;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	rc = dtl_enable(dtl);
236*4882a593Smuzhiyun 	if (rc)
237*4882a593Smuzhiyun 		return rc;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	filp->private_data = dtl;
240*4882a593Smuzhiyun 	return 0;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
dtl_file_release(struct inode * inode,struct file * filp)243*4882a593Smuzhiyun static int dtl_file_release(struct inode *inode, struct file *filp)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	struct dtl *dtl = inode->i_private;
246*4882a593Smuzhiyun 	dtl_disable(dtl);
247*4882a593Smuzhiyun 	return 0;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
dtl_file_read(struct file * filp,char __user * buf,size_t len,loff_t * pos)250*4882a593Smuzhiyun static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len,
251*4882a593Smuzhiyun 		loff_t *pos)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	long int rc, n_read, n_req, read_size;
254*4882a593Smuzhiyun 	struct dtl *dtl;
255*4882a593Smuzhiyun 	u64 cur_idx, last_idx, i;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	if ((len % sizeof(struct dtl_entry)) != 0)
258*4882a593Smuzhiyun 		return -EINVAL;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	dtl = filp->private_data;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	/* requested number of entries to read */
263*4882a593Smuzhiyun 	n_req = len / sizeof(struct dtl_entry);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	/* actual number of entries read */
266*4882a593Smuzhiyun 	n_read = 0;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	spin_lock(&dtl->lock);
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	cur_idx = dtl_current_index(dtl);
271*4882a593Smuzhiyun 	last_idx = dtl->last_idx;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	if (last_idx + dtl->buf_entries <= cur_idx)
274*4882a593Smuzhiyun 		last_idx = cur_idx - dtl->buf_entries + 1;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	if (last_idx + n_req > cur_idx)
277*4882a593Smuzhiyun 		n_req = cur_idx - last_idx;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	if (n_req > 0)
280*4882a593Smuzhiyun 		dtl->last_idx = last_idx + n_req;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	spin_unlock(&dtl->lock);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	if (n_req <= 0)
285*4882a593Smuzhiyun 		return 0;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	i = last_idx % dtl->buf_entries;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	/* read the tail of the buffer if we've wrapped */
290*4882a593Smuzhiyun 	if (i + n_req > dtl->buf_entries) {
291*4882a593Smuzhiyun 		read_size = dtl->buf_entries - i;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 		rc = copy_to_user(buf, &dtl->buf[i],
294*4882a593Smuzhiyun 				read_size * sizeof(struct dtl_entry));
295*4882a593Smuzhiyun 		if (rc)
296*4882a593Smuzhiyun 			return -EFAULT;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 		i = 0;
299*4882a593Smuzhiyun 		n_req -= read_size;
300*4882a593Smuzhiyun 		n_read += read_size;
301*4882a593Smuzhiyun 		buf += read_size * sizeof(struct dtl_entry);
302*4882a593Smuzhiyun 	}
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	/* .. and now the head */
305*4882a593Smuzhiyun 	rc = copy_to_user(buf, &dtl->buf[i], n_req * sizeof(struct dtl_entry));
306*4882a593Smuzhiyun 	if (rc)
307*4882a593Smuzhiyun 		return -EFAULT;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	n_read += n_req;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	return n_read * sizeof(struct dtl_entry);
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun static const struct file_operations dtl_fops = {
315*4882a593Smuzhiyun 	.open		= dtl_file_open,
316*4882a593Smuzhiyun 	.release	= dtl_file_release,
317*4882a593Smuzhiyun 	.read		= dtl_file_read,
318*4882a593Smuzhiyun 	.llseek		= no_llseek,
319*4882a593Smuzhiyun };
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun static struct dentry *dtl_dir;
322*4882a593Smuzhiyun 
dtl_setup_file(struct dtl * dtl)323*4882a593Smuzhiyun static void dtl_setup_file(struct dtl *dtl)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	char name[10];
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	sprintf(name, "cpu-%d", dtl->cpu);
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops);
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
dtl_init(void)332*4882a593Smuzhiyun static int dtl_init(void)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	int i;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	if (!firmware_has_feature(FW_FEATURE_SPLPAR))
337*4882a593Smuzhiyun 		return -ENODEV;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	/* set up common debugfs structure */
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	dtl_dir = debugfs_create_dir("dtl", powerpc_debugfs_root);
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	debugfs_create_x8("dtl_event_mask", 0600, dtl_dir, &dtl_event_mask);
344*4882a593Smuzhiyun 	debugfs_create_u32("dtl_buf_entries", 0400, dtl_dir, &dtl_buf_entries);
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	/* set up the per-cpu log structures */
347*4882a593Smuzhiyun 	for_each_possible_cpu(i) {
348*4882a593Smuzhiyun 		struct dtl *dtl = &per_cpu(cpu_dtl, i);
349*4882a593Smuzhiyun 		spin_lock_init(&dtl->lock);
350*4882a593Smuzhiyun 		dtl->cpu = i;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 		dtl_setup_file(dtl);
353*4882a593Smuzhiyun 	}
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	return 0;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun machine_arch_initcall(pseries, dtl_init);
358