blob: fe24d15c6a83e934988f5d3f7e05314ba268624e [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14/* #define DEBUG */
15#define ALIGN_CPU
16
17#include <linux/spinlock.h>
18#include <linux/debugfs.h>
19#include <linux/relay.h>
20#include <linux/slab.h>
21#include <linux/time.h>
22#include <linux/sched.h>
23
24#include "kgsl.h"
25#include "kgsl_cffdump.h"
26#include "kgsl_debugfs.h"
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060027#include "kgsl_log.h"
28#include "kgsl_sharedmem.h"
29#include "adreno_pm4types.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31static struct rchan *chan;
32static struct dentry *dir;
33static int suspended;
34static size_t dropped;
35static size_t subbuf_size = 256*1024;
36static size_t n_subbufs = 64;
37
38/* forward declarations */
39static void destroy_channel(void);
40static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs);
41
42static spinlock_t cffdump_lock;
43static ulong serial_nr;
44static ulong total_bytes;
45static ulong total_syncmem;
46static long last_sec;
47
48#define MEMBUF_SIZE 64
49
50#define CFF_OP_WRITE_REG 0x00000002
51struct cff_op_write_reg {
52 unsigned char op;
53 uint addr;
54 uint value;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060055} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056
57#define CFF_OP_POLL_REG 0x00000004
58struct cff_op_poll_reg {
59 unsigned char op;
60 uint addr;
61 uint value;
62 uint mask;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060063} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064
65#define CFF_OP_WAIT_IRQ 0x00000005
66struct cff_op_wait_irq {
67 unsigned char op;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060068} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069
70#define CFF_OP_VERIFY_MEM_FILE 0x00000007
71#define CFF_OP_RMW 0x0000000a
72
73#define CFF_OP_WRITE_MEM 0x0000000b
74struct cff_op_write_mem {
75 unsigned char op;
76 uint addr;
77 uint value;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060078} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079
80#define CFF_OP_WRITE_MEMBUF 0x0000000c
81struct cff_op_write_membuf {
82 unsigned char op;
83 uint addr;
84 ushort count;
85 uint buffer[MEMBUF_SIZE];
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060086} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087
88#define CFF_OP_EOF 0xffffffff
89struct cff_op_eof {
90 unsigned char op;
Sushmita Susheelendra22d87172011-05-09 16:40:02 -060091} __packed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092
93
94static void b64_encodeblock(unsigned char in[3], unsigned char out[4], int len)
95{
96 static const char tob64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno"
97 "pqrstuvwxyz0123456789+/";
98
99 out[0] = tob64[in[0] >> 2];
100 out[1] = tob64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
101 out[2] = (unsigned char) (len > 1 ? tob64[((in[1] & 0x0f) << 2)
102 | ((in[2] & 0xc0) >> 6)] : '=');
103 out[3] = (unsigned char) (len > 2 ? tob64[in[2] & 0x3f] : '=');
104}
105
106static void b64_encode(const unsigned char *in_buf, int in_size,
107 unsigned char *out_buf, int out_bufsize, int *out_size)
108{
109 unsigned char in[3], out[4];
110 int i, len;
111
112 *out_size = 0;
113 while (in_size > 0) {
114 len = 0;
115 for (i = 0; i < 3; ++i) {
116 if (in_size-- > 0) {
117 in[i] = *in_buf++;
118 ++len;
119 } else
120 in[i] = 0;
121 }
122 if (len) {
123 b64_encodeblock(in, out, len);
124 if (out_bufsize < 4) {
125 pr_warn("kgsl: cffdump: %s: out of buffer\n",
126 __func__);
127 return;
128 }
129 for (i = 0; i < 4; ++i)
130 *out_buf++ = out[i];
131 *out_size += 4;
132 out_bufsize -= 4;
133 }
134 }
135}
136
137#define KLOG_TMPBUF_SIZE (1024)
138static void klog_printk(const char *fmt, ...)
139{
140 /* per-cpu klog formatting temporary buffer */
141 static char klog_buf[NR_CPUS][KLOG_TMPBUF_SIZE];
142
143 va_list args;
144 int len;
145 char *cbuf;
146 unsigned long flags;
147
148 local_irq_save(flags);
149 cbuf = klog_buf[smp_processor_id()];
150 va_start(args, fmt);
151 len = vsnprintf(cbuf, KLOG_TMPBUF_SIZE, fmt, args);
152 total_bytes += len;
153 va_end(args);
154 relay_write(chan, cbuf, len);
155 local_irq_restore(flags);
156}
157
158static struct cff_op_write_membuf cff_op_write_membuf;
159static void cffdump_membuf(int id, unsigned char *out_buf, int out_bufsize)
160{
161 void *data;
162 int len, out_size;
163 struct cff_op_write_mem cff_op_write_mem;
164
165 uint addr = cff_op_write_membuf.addr
166 - sizeof(uint)*cff_op_write_membuf.count;
167
168 if (!cff_op_write_membuf.count) {
169 pr_warn("kgsl: cffdump: membuf: count == 0, skipping");
170 return;
171 }
172
173 if (cff_op_write_membuf.count != 1) {
174 cff_op_write_membuf.op = CFF_OP_WRITE_MEMBUF;
175 cff_op_write_membuf.addr = addr;
176 len = sizeof(cff_op_write_membuf) -
177 sizeof(uint)*(MEMBUF_SIZE - cff_op_write_membuf.count);
178 data = &cff_op_write_membuf;
179 } else {
180 cff_op_write_mem.op = CFF_OP_WRITE_MEM;
181 cff_op_write_mem.addr = addr;
182 cff_op_write_mem.value = cff_op_write_membuf.buffer[0];
183 data = &cff_op_write_mem;
184 len = sizeof(cff_op_write_mem);
185 }
186 b64_encode(data, len, out_buf, out_bufsize, &out_size);
187 out_buf[out_size] = 0;
188 klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
189 cff_op_write_membuf.count = 0;
190 cff_op_write_membuf.addr = 0;
191}
192
193static void cffdump_printline(int id, uint opcode, uint op1, uint op2,
194 uint op3)
195{
196 struct cff_op_write_reg cff_op_write_reg;
197 struct cff_op_poll_reg cff_op_poll_reg;
198 struct cff_op_wait_irq cff_op_wait_irq;
199 struct cff_op_eof cff_op_eof;
200 unsigned char out_buf[sizeof(cff_op_write_membuf)/3*4 + 16];
201 void *data;
202 int len = 0, out_size;
203 long cur_secs;
204
205 spin_lock(&cffdump_lock);
206 if (opcode == CFF_OP_WRITE_MEM) {
207 if (op1 < 0x40000000 || op1 >= 0x60000000)
208 KGSL_CORE_ERR("addr out-of-range: op1=%08x", op1);
209 if ((cff_op_write_membuf.addr != op1 &&
210 cff_op_write_membuf.count)
211 || (cff_op_write_membuf.count == MEMBUF_SIZE))
212 cffdump_membuf(id, out_buf, sizeof(out_buf));
213
214 cff_op_write_membuf.buffer[cff_op_write_membuf.count++] = op2;
215 cff_op_write_membuf.addr = op1 + sizeof(uint);
216 spin_unlock(&cffdump_lock);
217 return;
218 } else if (cff_op_write_membuf.count)
219 cffdump_membuf(id, out_buf, sizeof(out_buf));
220 spin_unlock(&cffdump_lock);
221
222 switch (opcode) {
223 case CFF_OP_WRITE_REG:
224 cff_op_write_reg.op = opcode;
225 cff_op_write_reg.addr = op1;
226 cff_op_write_reg.value = op2;
227 data = &cff_op_write_reg;
228 len = sizeof(cff_op_write_reg);
229 break;
230
231 case CFF_OP_POLL_REG:
232 cff_op_poll_reg.op = opcode;
233 cff_op_poll_reg.addr = op1;
234 cff_op_poll_reg.value = op2;
235 cff_op_poll_reg.mask = op3;
236 data = &cff_op_poll_reg;
237 len = sizeof(cff_op_poll_reg);
238 break;
239
240 case CFF_OP_WAIT_IRQ:
241 cff_op_wait_irq.op = opcode;
242 data = &cff_op_wait_irq;
243 len = sizeof(cff_op_wait_irq);
244 break;
245
246 case CFF_OP_EOF:
247 cff_op_eof.op = opcode;
248 data = &cff_op_eof;
249 len = sizeof(cff_op_eof);
250 break;
251 }
252
253 if (len) {
254 b64_encode(data, len, out_buf, sizeof(out_buf), &out_size);
255 out_buf[out_size] = 0;
256 klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
257 } else
258 pr_warn("kgsl: cffdump: unhandled opcode: %d\n", opcode);
259
260 cur_secs = get_seconds();
261 if ((cur_secs - last_sec) > 10 || (last_sec - cur_secs) > 10) {
262 pr_info("kgsl: cffdump: total [bytes:%lu kB, syncmem:%lu kB], "
263 "seq#: %lu\n", total_bytes/1024, total_syncmem/1024,
264 serial_nr);
265 last_sec = cur_secs;
266 }
267}
268
269void kgsl_cffdump_init()
270{
271 struct dentry *debugfs_dir = kgsl_get_debugfs_dir();
272
273#ifdef ALIGN_CPU
274 cpumask_t mask;
275
276 cpumask_clear(&mask);
Lucille Sylvester57996e32011-05-26 19:12:22 -0600277 cpumask_set_cpu(0, &mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 sched_setaffinity(0, &mask);
279#endif
280 if (!debugfs_dir || IS_ERR(debugfs_dir)) {
281 KGSL_CORE_ERR("Debugfs directory is bad\n");
282 return;
283 }
284
285 kgsl_cff_dump_enable = 1;
286
287 spin_lock_init(&cffdump_lock);
288
289 dir = debugfs_create_dir("cff", debugfs_dir);
290 if (!dir) {
291 KGSL_CORE_ERR("debugfs_create_dir failed\n");
292 return;
293 }
294
295 chan = create_channel(subbuf_size, n_subbufs);
296}
297
298void kgsl_cffdump_destroy()
299{
300 if (chan)
301 relay_flush(chan);
302 destroy_channel();
303 if (dir)
304 debugfs_remove(dir);
305}
306
307void kgsl_cffdump_open(enum kgsl_deviceid device_id)
308{
309}
310
311void kgsl_cffdump_close(enum kgsl_deviceid device_id)
312{
313 cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0);
314}
315
316void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
317 const struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes,
318 bool clean_cache)
319{
320 const void *src;
321 uint host_size;
322 uint physaddr;
323
324 if (!kgsl_cff_dump_enable)
325 return;
326
327 total_syncmem += sizebytes;
328
329 if (memdesc == NULL) {
330 struct kgsl_mem_entry *entry;
331 spin_lock(&dev_priv->process_priv->mem_lock);
332 entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
333 gpuaddr, sizebytes);
334 spin_unlock(&dev_priv->process_priv->mem_lock);
335 if (entry == NULL) {
336 KGSL_CORE_ERR("did not find mapping "
337 "for gpuaddr: 0x%08x\n", gpuaddr);
338 return;
339 }
340 memdesc = &entry->memdesc;
341 }
342 BUG_ON(memdesc->gpuaddr == 0);
343 BUG_ON(gpuaddr == 0);
344 physaddr = kgsl_get_realaddr(memdesc) + (gpuaddr - memdesc->gpuaddr);
345
346 src = kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size);
347 if (src == NULL || host_size < sizebytes) {
Sushmita Susheelendra22d87172011-05-09 16:40:02 -0600348 KGSL_CORE_ERR("did not find mapping for "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 "gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n",
350 gpuaddr, memdesc->hostptr, memdesc->physaddr);
351 return;
352 }
353
354 if (clean_cache) {
355 /* Ensure that this memory region is not read from the
356 * cache but fetched fresh */
357
358 mb();
359
Sushmita Susheelendra22d87172011-05-09 16:40:02 -0600360 kgsl_cache_range_op((struct kgsl_memdesc *)memdesc,
361 KGSL_CACHE_OP_INV);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 }
363
364 BUG_ON(physaddr > 0x66000000 && physaddr < 0x66ffffff);
365 while (sizebytes > 3) {
366 cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src,
367 0);
368 physaddr += 4;
369 src += 4;
370 sizebytes -= 4;
371 }
372 if (sizebytes > 0)
373 cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src,
374 0);
375}
376
377void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes)
378{
379 if (!kgsl_cff_dump_enable)
380 return;
381
382 BUG_ON(addr > 0x66000000 && addr < 0x66ffffff);
383 while (sizebytes > 3) {
384 /* Use 32bit memory writes as long as there's at least
385 * 4 bytes left */
386 cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0);
387 addr += 4;
388 sizebytes -= 4;
389 }
390 if (sizebytes > 0)
391 cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0);
392}
393
394void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr,
395 uint value)
396{
397 if (!kgsl_cff_dump_enable)
398 return;
399
400 cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value, 0);
401}
402
403void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr,
404 uint value, uint mask)
405{
406 if (!kgsl_cff_dump_enable)
407 return;
408
409 cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value, mask);
410}
411
412void kgsl_cffdump_slavewrite(uint addr, uint value)
413{
414 if (!kgsl_cff_dump_enable)
415 return;
416
417 cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0);
418}
419
420int kgsl_cffdump_waitirq(void)
421{
422 if (!kgsl_cff_dump_enable)
423 return 0;
424
425 cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0);
426
427 return 1;
428}
429EXPORT_SYMBOL(kgsl_cffdump_waitirq);
430
431#define ADDRESS_STACK_SIZE 256
432#define GET_PM4_TYPE3_OPCODE(x) ((*(x) >> 8) & 0xFF)
433static unsigned int kgsl_cffdump_addr_count;
434
435static bool kgsl_cffdump_handle_type3(struct kgsl_device_private *dev_priv,
436 uint *hostaddr, bool check_only)
437{
438 static uint addr_stack[ADDRESS_STACK_SIZE];
439 static uint size_stack[ADDRESS_STACK_SIZE];
440
441 switch (GET_PM4_TYPE3_OPCODE(hostaddr)) {
442 case PM4_INDIRECT_BUFFER_PFD:
443 case PM4_INDIRECT_BUFFER:
444 {
445 /* traverse indirect buffers */
446 int i;
447 uint ibaddr = hostaddr[1];
448 uint ibsize = hostaddr[2];
449
450 /* is this address already in encountered? */
451 for (i = 0;
452 i < kgsl_cffdump_addr_count && addr_stack[i] != ibaddr;
453 ++i)
454 ;
455
456 if (kgsl_cffdump_addr_count == i) {
457 addr_stack[kgsl_cffdump_addr_count] = ibaddr;
458 size_stack[kgsl_cffdump_addr_count++] = ibsize;
459
460 if (kgsl_cffdump_addr_count >= ADDRESS_STACK_SIZE) {
461 KGSL_CORE_ERR("stack overflow\n");
462 return false;
463 }
464
465 return kgsl_cffdump_parse_ibs(dev_priv, NULL,
466 ibaddr, ibsize, check_only);
467 } else if (size_stack[i] != ibsize) {
468 KGSL_CORE_ERR("gpuaddr: 0x%08x, "
469 "wc: %u, with size wc: %u already on the "
470 "stack\n", ibaddr, ibsize, size_stack[i]);
471 return false;
472 }
473 }
474 break;
475 }
476
477 return true;
478}
479
480/*
481 * Traverse IBs and dump them to test vector. Detect swap by inspecting
482 * register writes, keeping note of the current state, and dump
483 * framebuffer config to test vector
484 */
485bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv,
486 const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords,
487 bool check_only)
488{
489 static uint level; /* recursion level */
490 bool ret = true;
491 uint host_size;
492 uint *hostaddr, *hoststart;
493 int dwords_left = sizedwords; /* dwords left in the current command
494 buffer */
495
496 if (level == 0)
497 kgsl_cffdump_addr_count = 0;
498
499 if (memdesc == NULL) {
500 struct kgsl_mem_entry *entry;
501 spin_lock(&dev_priv->process_priv->mem_lock);
502 entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
503 gpuaddr, sizedwords * sizeof(uint));
504 spin_unlock(&dev_priv->process_priv->mem_lock);
505 if (entry == NULL) {
506 KGSL_CORE_ERR("did not find mapping "
507 "for gpuaddr: 0x%08x\n", gpuaddr);
508 return true;
509 }
510 memdesc = &entry->memdesc;
511 }
512
513 hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size);
514 if (hostaddr == NULL) {
515 KGSL_CORE_ERR("did not find mapping for "
516 "gpuaddr: 0x%08x\n", gpuaddr);
517 return true;
518 }
519
520 hoststart = hostaddr;
521
522 level++;
523
524 if (!memdesc->physaddr) {
525 KGSL_CORE_ERR("no physaddr");
526 return true;
527 } else {
528 mb();
Sushmita Susheelendra22d87172011-05-09 16:40:02 -0600529 kgsl_cache_range_op((struct kgsl_memdesc *)memdesc,
530 KGSL_CACHE_OP_INV);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531 }
532
533#ifdef DEBUG
534 pr_info("kgsl: cffdump: ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n",
535 gpuaddr, sizedwords, hostaddr);
536#endif
537
538 while (dwords_left > 0) {
539 int count = 0; /* dword count including packet header */
540 bool cur_ret = true;
541
542 switch (*hostaddr >> 30) {
543 case 0x0: /* type-0 */
544 count = (*hostaddr >> 16)+2;
545 break;
546 case 0x1: /* type-1 */
547 count = 2;
548 break;
549 case 0x3: /* type-3 */
550 count = ((*hostaddr >> 16) & 0x3fff) + 2;
551 cur_ret = kgsl_cffdump_handle_type3(dev_priv,
552 hostaddr, check_only);
553 break;
554 default:
555 pr_warn("kgsl: cffdump: parse-ib: unexpected type: "
556 "type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n",
557 *hostaddr >> 30, *hostaddr, hostaddr,
558 gpuaddr+4*(sizedwords-dwords_left));
559 cur_ret = false;
560 count = dwords_left;
561 break;
562 }
563
564#ifdef DEBUG
565 if (!cur_ret) {
566 pr_info("kgsl: cffdump: bad sub-type: #:%d/%d, v:0x%08x"
567 " @ 0x%p[gb:0x%08x], level:%d\n",
568 sizedwords-dwords_left, sizedwords, *hostaddr,
569 hostaddr, gpuaddr+4*(sizedwords-dwords_left),
570 level);
571
572 print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
573 DUMP_PREFIX_OFFSET, 32, 4, hoststart,
574 sizedwords*4, 0);
575 }
576#endif
577 ret = ret && cur_ret;
578
579 /* jump to next packet */
580 dwords_left -= count;
581 hostaddr += count;
582 cur_ret = dwords_left >= 0;
583
584#ifdef DEBUG
585 if (!cur_ret) {
586 pr_info("kgsl: cffdump: bad count: c:%d, #:%d/%d, "
587 "v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n",
588 count, sizedwords-(dwords_left+count),
589 sizedwords, *(hostaddr-count), hostaddr-count,
590 gpuaddr+4*(sizedwords-(dwords_left+count)),
591 level);
592
593 print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
594 DUMP_PREFIX_OFFSET, 32, 4, hoststart,
595 sizedwords*4, 0);
596 }
597#endif
598
599 ret = ret && cur_ret;
600 }
601
602 if (!ret)
603 pr_info("kgsl: cffdump: parsing failed: gpuaddr:0x%08x, "
604 "host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords);
605
606 if (!check_only) {
607#ifdef DEBUG
608 uint offset = gpuaddr - memdesc->gpuaddr;
609 pr_info("kgsl: cffdump: ib-dump: hostptr:%p, gpuaddr:%08x, "
610 "physaddr:%08x, offset:%d, size:%d", hoststart,
611 gpuaddr, memdesc->physaddr + offset, offset,
612 sizedwords*4);
613#endif
614 kgsl_cffdump_syncmem(dev_priv, memdesc, gpuaddr, sizedwords*4,
615 false);
616 }
617
618 level--;
619
620 return ret;
621}
622
623static int subbuf_start_handler(struct rchan_buf *buf,
624 void *subbuf, void *prev_subbuf, uint prev_padding)
625{
626 pr_debug("kgsl: cffdump: subbuf_start_handler(subbuf=%p, prev_subbuf"
627 "=%p, prev_padding=%08x)\n", subbuf, prev_subbuf, prev_padding);
628
629 if (relay_buf_full(buf)) {
630 if (!suspended) {
631 suspended = 1;
632 pr_warn("kgsl: cffdump: relay: cpu %d buffer full!!!\n",
633 smp_processor_id());
634 }
635 dropped++;
636 return 0;
637 } else if (suspended) {
638 suspended = 0;
639 pr_warn("kgsl: cffdump: relay: cpu %d buffer no longer full.\n",
640 smp_processor_id());
641 }
642
643 subbuf_start_reserve(buf, 0);
644 return 1;
645}
646
647static struct dentry *create_buf_file_handler(const char *filename,
648 struct dentry *parent, int mode, struct rchan_buf *buf,
649 int *is_global)
650{
651 return debugfs_create_file(filename, mode, parent, buf,
652 &relay_file_operations);
653}
654
655/*
656 * file_remove() default callback. Removes relay file in debugfs.
657 */
658static int remove_buf_file_handler(struct dentry *dentry)
659{
660 pr_info("kgsl: cffdump: %s()\n", __func__);
661 debugfs_remove(dentry);
662 return 0;
663}
664
665/*
666 * relay callbacks
667 */
668static struct rchan_callbacks relay_callbacks = {
669 .subbuf_start = subbuf_start_handler,
670 .create_buf_file = create_buf_file_handler,
671 .remove_buf_file = remove_buf_file_handler,
672};
673
674/**
675 * create_channel - creates channel /debug/klog/cpuXXX
676 *
677 * Creates channel along with associated produced/consumed control files
678 *
679 * Returns channel on success, NULL otherwise
680 */
681static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs)
682{
683 struct rchan *chan;
684
685 pr_info("kgsl: cffdump: relay: create_channel: subbuf_size %u, "
686 "n_subbufs %u, dir 0x%p\n", subbuf_size, n_subbufs, dir);
687
688 chan = relay_open("cpu", dir, subbuf_size,
689 n_subbufs, &relay_callbacks, NULL);
690 if (!chan) {
691 KGSL_CORE_ERR("relay_open failed\n");
692 return NULL;
693 }
694
695 suspended = 0;
696 dropped = 0;
697
698 return chan;
699}
700
701/**
702 * destroy_channel - destroys channel /debug/kgsl/cff/cpuXXX
703 *
704 * Destroys channel along with associated produced/consumed control files
705 */
706static void destroy_channel(void)
707{
708 pr_info("kgsl: cffdump: relay: destroy_channel\n");
709 if (chan) {
710 relay_close(chan);
711 chan = NULL;
712 }
713}
714