fixes for bc_cat
[sgx.git] / pvr / pvr_trace_cmd.c
1 /*
2  * Copyright (C) 2011 Nokia Corporation
3  * Author: Imre Deak <imre.deak@nokia.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19
20 #include <linux/vmalloc.h>
21
22 #include "img_types.h"
23 #include "resman.h"
24 #include "handle.h"
25 #include "pvr_trace_cmd.h"
26 #include "perproc.h"
27 #include "pvr_bridge_km.h"
28
29 /* Need to be log2 size. */
30 #define PVR_TBUF_SIZE   (1 << (CONFIG_PVR_TRACE_CMD_BUF_SHIFT + PAGE_SHIFT))
31
32 #define PVR_TRCMD_INDENT        3
33
34 static struct pvr_trcmd_buf {
35         int             read_idx;
36         int             write_idx;
37         char            data[PVR_TBUF_SIZE];
38 } tbuf;
39
40 DEFINE_MUTEX(pvr_trcmd_mutex);  /* protects tbuf */
41
42 struct tbuf_frame {
43         unsigned short size;
44         unsigned short type;
45         unsigned long pid;
46         unsigned long long time;
47         char pname[16];
48 };
49
50 struct trcmd_desc {
51         const char *name;
52         size_t (*print)(char *dst, size_t dst_size, const void *tbuf);
53 };
54
55 static size_t prn_syn(const char *name, char *dst, size_t dst_size,
56                       const struct pvr_trcmd_syn *ts)
57 {
58         size_t len;
59
60         if (!ts->addr)
61                 return 0;
62
63         len =  scnprintf(dst, dst_size, "%*s%s", PVR_TRCMD_INDENT, "", name);
64         len += scnprintf(&dst[len], dst_size - len, " addr:%08lx", ts->addr);
65         len += scnprintf(&dst[len], dst_size - len,
66                          " rop/c:%8lu/%8lu wop/c:%8lu/%8lu\n",
67                          ts->rd_pend, ts->rd_comp, ts->wr_pend, ts->wr_comp);
68
69         return len;
70 }
71
72 static size_t trcmd_prn_syn(char *dst, size_t dst_size, const void *tbuf)
73 {
74         const struct pvr_trcmd_syn *ts = tbuf;
75
76         return prn_syn("syn     ", dst, dst_size, ts);
77 }
78
79 static size_t trcmd_prn_sgxkick(char *dst, size_t dst_size, const void *tbuf)
80 {
81         const struct pvr_trcmd_sgxkick *d = tbuf;
82         size_t len;
83         int i;
84
85         len  = prn_syn("tatq_syn", dst, dst_size, &d->tatq_syn);
86         len += prn_syn("3dtq_syn", &dst[len], dst_size - len, &d->_3dtq_syn);
87         for (i = 0; i < SGX_MAX_SRC_SYNCS; i++) {
88                 char sname[10];
89
90                 snprintf(sname, sizeof(sname), "src_syn%d", i);
91                 len += prn_syn(sname, &dst[len], dst_size - len,
92                                &d->src_syn[i]);
93         }
94         len += prn_syn("dst_syn ", &dst[len], dst_size - len, &d->dst_syn);
95         len += prn_syn("ta3d_syn", &dst[len], dst_size - len, &d->ta3d_syn);
96         len += scnprintf(&dst[len], dst_size - len, "%*sctx  %08lx\n",
97                          PVR_TRCMD_INDENT, "", d->ctx);
98
99         return len;
100 }
101
102 static size_t trcmd_prn_sgxtfer(char *dst, size_t dst_size, const void *tbuf)
103 {
104         const struct pvr_trcmd_sgxtransfer *d = tbuf;
105         size_t len;
106
107         len  = prn_syn("ta_syn  ", dst, dst_size, &d->ta_syn);
108         len += prn_syn("3d_syn  ", &dst[len], dst_size - len, &d->_3d_syn);
109         len += prn_syn("src_syn ", &dst[len], dst_size - len, &d->src_syn);
110         len += prn_syn("dst_syn ", &dst[len], dst_size - len, &d->dst_syn);
111         len += scnprintf(&dst[len], dst_size - len, "%*sctx  %08lx\n",
112                          PVR_TRCMD_INDENT, "", d->ctx);
113
114         return len;
115 }
116
117 static struct trcmd_desc trcmd_desc_table[] = {
118         [PVR_TRCMD_SGX_FIRSTKICK]    = { "sgx_first_kick", trcmd_prn_sgxkick },
119         [PVR_TRCMD_SGX_KICK]         = { "sgx_kick", trcmd_prn_sgxkick },
120         [PVR_TRCMD_TFER_KICK]        = { "sgx_tfer_kick", trcmd_prn_sgxtfer },
121         [PVR_TRCMD_SGX_QBLT_FLPREQ]  = { "sgx_qblt_flip", NULL },
122         [PVR_TRCMD_SGX_QBLT_UPDREQ]  = { "sgx_qblt_update", NULL },
123         [PVR_TRCMD_SGX_QBLT_SYNCHK]  = { "sgx_qblt_synchk", trcmd_prn_syn },
124         [PVR_TRCMD_SGX_QBLT_SYNREQ]  = { "sgx_qblt_synreq", trcmd_prn_syn },
125         [PVR_TRCMD_SGX_QBLT_SYNCOMP] = { "sgx_qblt_syn_comp", trcmd_prn_syn },
126         [PVR_TRCMD_SGX_QBLT_FLPCOMP] = { "sgx_qblt_flip_comp", NULL },
127         [PVR_TRCMD_SGX_QBLT_UPDCOMP] = { "sgx_qblt_update_comp", NULL }
128 };
129
130 /* Modular add */
131 static inline int tbuf_idx_add(int val, int delta)
132 {
133         val += delta;
134         val &= PVR_TBUF_SIZE - 1;
135
136         return val;
137 }
138
139 static size_t prn_frame(const struct tbuf_frame *f, char *dst, size_t dst_size)
140 {
141         const struct trcmd_desc *desc;
142         unsigned long long sec;
143         unsigned long usec_frac;
144         size_t len;
145
146         desc = &trcmd_desc_table[f->type];
147
148         sec = f->time;
149         usec_frac = do_div(sec, 1000000000) / 1000;
150
151         len = scnprintf(dst, dst_size, "[%5llu.%06lu] %s[%ld]: %s\n",
152                         sec, usec_frac, f->pname, f->pid, desc->name);
153         if (desc->print)
154                 len += desc->print(&dst[len], dst_size - len, (void *)(f + 1));
155
156         return len;
157 }
158
159 int pvr_trcmd_create_snapshot(u8 **snapshot_ret, size_t *snapshot_size)
160 {
161         u8 *snapshot;
162         int read_idx;
163         size_t size;
164         size_t tail_size;
165
166         read_idx = tbuf.read_idx;
167         size = tbuf_idx_add(tbuf.write_idx, -read_idx);
168         snapshot = vmalloc(size);
169         if (!snapshot)
170                 return -ENOMEM;
171
172         tail_size = min_t(size_t, size, PVR_TBUF_SIZE - read_idx);
173         memcpy(snapshot, &tbuf.data[read_idx], tail_size);
174         memcpy(&snapshot[tail_size], tbuf.data, size - tail_size);
175
176         *snapshot_ret = snapshot;
177         *snapshot_size = size;
178
179         return 0;
180 }
181
182 void pvr_trcmd_destroy_snapshot(void *snapshot)
183 {
184         vfree(snapshot);
185 }
186
187 size_t pvr_trcmd_print(char *dst, size_t dst_size, const u8 *snapshot,
188                        size_t snapshot_size, loff_t *snapshot_ofs)
189 {
190         size_t dst_len;
191
192         if (*snapshot_ofs >= snapshot_size)
193                 return 0;
194         dst_len = 0;
195
196         snapshot_size -= *snapshot_ofs;
197
198         while (snapshot_size) {
199                 const struct tbuf_frame *f;
200                 size_t this_len;
201
202                 if (WARN_ON_ONCE(snapshot_size < 4))
203                         break;
204
205                 f = (struct tbuf_frame *)&snapshot[*snapshot_ofs];
206                 if (WARN_ON_ONCE(!f->size || f->size > snapshot_size ||
207                                  f->type >= ARRAY_SIZE(trcmd_desc_table)))
208                         break;
209
210                 if (f->type != PVR_TRCMD_PAD)
211                         this_len = prn_frame(f, &dst[dst_len],
212                                              dst_size - dst_len);
213                 else
214                         this_len = 0;
215
216                 if (dst_len + this_len + 1 == dst_size) {
217                         /* drop the last printed frame */
218                         dst[dst_len] = '\0';
219
220                         break;
221                 }
222
223                 *snapshot_ofs += f->size;
224                 dst_len += this_len;
225                 snapshot_size -= f->size;
226         }
227
228         return dst_len;
229 }
230
231 static void *tbuf_get_space(size_t size)
232 {
233         void *ret;
234         int buf_idx;
235
236         while (1) {
237                 if (tbuf_idx_add(tbuf.read_idx - 1, -tbuf.write_idx) < size) {
238                         /*
239                          * Trace buffer overflow, discard the frame that will
240                          * be overwritten by the next write.
241                          */
242                         struct tbuf_frame *f =
243                                 (void *)&tbuf.data[tbuf.read_idx];
244
245                         buf_idx = tbuf.read_idx;
246                         tbuf.read_idx = tbuf_idx_add(tbuf.read_idx,
247                                                           f->size);
248                 } else if (PVR_TBUF_SIZE - tbuf.write_idx < size) {
249                         struct tbuf_frame *f =
250                                 (void *)&tbuf.data[tbuf.write_idx];
251                         /*
252                          * Not enough space until the end of trace buffer,
253                          * rewind to the beginning. Frames are sizeof(long)
254                          * aligned, thus we are guaranteed to have space for
255                          * the following two fields.
256                          */
257                         f->size = PVR_TBUF_SIZE - tbuf.write_idx;
258                         f->type = PVR_TRCMD_PAD;
259                         tbuf.write_idx = 0;
260                 } else {
261                         break;
262                 }
263         }
264         ret = &tbuf.data[tbuf.write_idx];
265         tbuf.write_idx = tbuf_idx_add(tbuf.write_idx, size);
266
267         return ret;
268 }
269
270 void *pvr_trcmd_alloc(unsigned type, int pid, const char *pname, size_t size)
271 {
272         struct tbuf_frame *f;
273         size_t total_size;
274
275         size = ALIGN(size, __alignof__(*f));
276         total_size = sizeof(*f) + size;
277         f = tbuf_get_space(total_size);
278         f->size = total_size;
279         f->type = type;
280         f->pid = pid;
281         f->time = cpu_clock(smp_processor_id());
282         strlcpy(f->pname, pname, sizeof(f->pname));
283
284         return f + 1;
285 }
286
287 void pvr_trcmd_set_syn(struct pvr_trcmd_syn *ts,
288                      const struct PVRSRV_KERNEL_SYNC_INFO *si)
289 {
290         struct PVRSRV_SYNC_DATA *sd = si->psSyncData;
291
292         ts->rd_pend = sd->ui32ReadOpsPending;
293         ts->rd_comp = sd->ui32ReadOpsComplete;
294         ts->wr_pend = sd->ui32WriteOpsPending;
295         ts->wr_comp = sd->ui32WriteOpsComplete;
296         ts->addr    = si->sWriteOpsCompleteDevVAddr.uiAddr - 4;
297 }