gpu: pvr: pdumpfs: add initial frame handling and debugfs support
[sgx.git] / pvr / pvr_pdumpfs.c
1 /*
2  * Copyright (c) 2010-2011 by Luc Verhaegen <libv@codethink.co.uk>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18
19 #include <linux/mutex.h>
20 #include <linux/uaccess.h>
21 #include <linux/debugfs.h>
22
23 #include "img_defs.h"
24 #include "services_headers.h"
25 #include "pvr_pdump.h"
26 #include "pvr_pdumpfs.h"
27
28 static struct mutex pdumpfs_mutex[1];
29
30 enum pdumpfs_mode {
31         PDUMPFS_MODE_DISABLED,
32         PDUMPFS_MODE_STANDARD,
33         PDUMPFS_MODE_FULL,
34 };
35
36 static enum pdumpfs_mode pdumpfs_mode =
37 #if defined(CONFIG_PVR_PDUMP_MODE_STANDARD)
38         PDUMPFS_MODE_STANDARD
39 #elif defined(CONFIG_PVR_PDUMP_MODE_FULL)
40         PDUMPFS_MODE_FULL
41 #else
42         PDUMPFS_MODE_DISABLED
43 #endif
44         ;
45
46 #define FRAME_PAGE_COUNT 2040
47
48 struct pdumpfs_frame {
49         struct pdumpfs_frame *next;
50
51         u32 pid;
52         u32 number;
53
54         size_t offset;
55         int page_count;
56         unsigned long pages[FRAME_PAGE_COUNT];
57 };
58
59 static u32 frame_count_max = CONFIG_PVR_PDUMP_INITIAL_MAX_FRAME_COUNT;
60 static u32 frame_count;
61
62 static struct pdumpfs_frame *frame_init;
63 static struct pdumpfs_frame *frame_stream;
64 static struct pdumpfs_frame *frame_current;
65
66 static struct pdumpfs_frame *
67 frame_create(void)
68 {
69         struct pdumpfs_frame *frame =
70                 kmalloc(sizeof(struct pdumpfs_frame), GFP_KERNEL);
71         if (!frame)
72                 return NULL;
73
74         memset(frame, 0, sizeof(struct pdumpfs_frame));
75
76         return frame;
77 }
78
79 static void
80 frame_destroy(struct pdumpfs_frame *frame)
81 {
82         int i;
83
84         if (!frame)
85                 return;
86
87         for (i = 0; i < frame->page_count; i++)
88                 free_page(frame->pages[i]);
89
90         kfree(frame);
91 }
92
93 static void
94 frame_destroy_all(void)
95 {
96         frame_current = NULL;
97
98         frame_destroy(frame_init);
99         frame_init = NULL;
100
101         while (frame_stream) {
102                 struct pdumpfs_frame *frame = frame_stream;
103
104                 frame_stream = frame->next;
105
106                 frame_destroy(frame);
107                 frame_count--;
108         }
109 }
110
111 static void
112 frame_cull(void)
113 {
114         while (frame_count > frame_count_max) {
115                 struct pdumpfs_frame *frame = frame_stream;
116
117                 frame_stream = frame->next;
118                 frame->next = NULL;
119
120                 frame_destroy(frame);
121                 frame_count--;
122         }
123 }
124
125 static int
126 frame_new(u32 pid, u32 number)
127 {
128         struct pdumpfs_frame *frame = frame_create();
129
130         if (!frame) {
131                 pr_err("%s: Failed to create frame.\n", __func__);
132                 return -ENOMEM;
133         }
134
135         frame->pid = pid;
136         frame->number = number;
137
138         if (!frame_init)
139                 frame_init = frame;
140         else {
141                 if (frame_current != frame_init)
142                         frame_current->next = frame;
143                 else
144                         frame_stream = frame;
145                 frame_count++;
146         }
147
148         frame_current = frame;
149
150         frame_cull();
151
152         return 0;
153 }
154
155 void
156 pdumpfs_frame_set(u32 pid, u32 frame)
157 {
158         mutex_lock(pdumpfs_mutex);
159
160         frame_new(pid, frame);
161
162         mutex_unlock(pdumpfs_mutex);
163 }
164
165 bool
166 pdumpfs_capture_enabled(void)
167 {
168         bool ret;
169
170         mutex_lock(pdumpfs_mutex);
171
172         if (pdumpfs_mode == PDUMPFS_MODE_FULL)
173                 ret = true;
174         else
175                 ret = false;
176
177         mutex_unlock(pdumpfs_mutex);
178
179         return ret;
180 }
181
182 bool
183 pdumpfs_flags_check(u32 flags)
184 {
185         bool ret;
186
187         if (flags & PDUMP_FLAGS_NEVER)
188                 return false;
189
190         mutex_lock(pdumpfs_mutex);
191
192         if (pdumpfs_mode == PDUMPFS_MODE_FULL)
193                 ret = true;
194         else if ((pdumpfs_mode == PDUMPFS_MODE_STANDARD) &&
195                  (flags & PDUMP_FLAGS_CONTINUOUS))
196                 ret = true;
197         else
198                 ret = false;
199
200         mutex_unlock(pdumpfs_mutex);
201
202         return ret;
203 }
204
205 static size_t
206 pdumpfs_frame_write_low(void *buffer, size_t size, bool from_user)
207 {
208         struct pdumpfs_frame *frame = frame_current;
209         size_t ret = 0;
210
211         while (size) {
212                 size_t count = size;
213                 size_t offset = frame->offset & ~PAGE_MASK;
214                 unsigned long page;
215
216                 if (!offset) {
217                         if (frame->page_count >= FRAME_PAGE_COUNT) {
218                                 pr_err("%s: Frame size overrun.\n", __func__);
219                                 return -ENOMEM;
220                         }
221
222                         page = __get_free_page(GFP_KERNEL);
223                         if (!page) {
224                                 pr_err("%s: failed to get free page.\n",
225                                        __func__);
226                                 return -ENOMEM;
227                         }
228
229                         frame->pages[frame->page_count] = page;
230                         frame->page_count++;
231                 } else
232                         page =
233                                 frame->pages[frame->page_count - 1];
234
235                 if (count > (PAGE_SIZE - offset))
236                         count = PAGE_SIZE - offset;
237
238                 if (from_user) {
239                         if (copy_from_user(((u8 *) page) + offset,
240                                            (void __user __force *) buffer,
241                                            count))
242                                 return -EINVAL;
243                 } else
244                         memcpy(((u8 *) page) + offset, buffer, count);
245
246                 buffer += count;
247                 size -= count;
248                 ret += count;
249                 frame->offset += count;
250         }
251         return ret;
252 }
253
254 static size_t
255 pdumpfs_frame_write(void *buffer, size_t size, bool from_user)
256 {
257         size_t ret;
258
259         if ((frame_current->offset + size) > (PAGE_SIZE * FRAME_PAGE_COUNT)) {
260                 u32 pid = OSGetCurrentProcessIDKM();
261                 struct task_struct *task;
262
263                 pr_err("Frame overrun!!!\n");
264
265                 ret = frame_new(pid, -1);
266                 if (ret)
267                         return ret;
268
269                 rcu_read_lock();
270                 task = pid_task(find_vpid(pid), PIDTYPE_PID);
271
272 #define FRAME_OVERRUN_MESSAGE "-- Starting forced frame caused by "
273                 pdumpfs_frame_write_low(FRAME_OVERRUN_MESSAGE,
274                                         sizeof(FRAME_OVERRUN_MESSAGE), false);
275                 pdumpfs_frame_write_low(task->comm, strlen(task->comm), false);
276                 pdumpfs_frame_write_low("\r\n", 2, false);
277
278                 pr_err("%s: Frame size overrun, caused by %d (%s)\n",
279                        __func__, pid, task->comm);
280
281                 rcu_read_unlock();
282         }
283
284         return pdumpfs_frame_write_low(buffer, size, from_user);
285 }
286
287 enum PVRSRV_ERROR
288 pdumpfs_write_data(void *buffer, size_t size, bool from_user)
289 {
290         mutex_lock(pdumpfs_mutex);
291
292         size = pdumpfs_frame_write(buffer, size, from_user);
293
294         mutex_unlock(pdumpfs_mutex);
295
296         if ((size >= 0) || (size == -ENOMEM))
297                 return PVRSRV_OK;
298         else
299                 return PVRSRV_ERROR_GENERIC;
300 }
301
302 void
303 pdumpfs_write_string(char *string)
304 {
305         mutex_lock(pdumpfs_mutex);
306
307         pdumpfs_frame_write(string, strlen(string), false);
308
309         mutex_unlock(pdumpfs_mutex);
310 }
311
312 /*
313  * DebugFS entries.
314  */
315 static const struct {
316         char *name;
317         enum pdumpfs_mode mode;
318 } pdumpfs_modes[] = {
319         {"disabled",   PDUMPFS_MODE_DISABLED},
320         {"standard",   PDUMPFS_MODE_STANDARD},
321         {"full",       PDUMPFS_MODE_FULL},
322         {NULL, PDUMPFS_MODE_DISABLED}
323 };
324
325 static ssize_t
326 pdumpfs_mode_read(struct file *filp, char __user *buf, size_t size,
327                   loff_t *f_pos)
328 {
329         char tmp[16];
330         int i;
331
332         tmp[0] = 0;
333
334         mutex_lock(pdumpfs_mutex);
335
336         for (i = 0; pdumpfs_modes[i].name; i++)
337                 if (pdumpfs_modes[i].mode == pdumpfs_mode)
338                         snprintf(tmp, sizeof(tmp), "%s\n",
339                                  pdumpfs_modes[i].name);
340
341         mutex_unlock(pdumpfs_mutex);
342
343         if (strlen(tmp) < *f_pos)
344                 return 0;
345
346         if ((strlen(tmp) + 1) < (*f_pos + size))
347                 size = strlen(tmp) + 1 - *f_pos;
348
349         if (copy_to_user(buf, tmp + *f_pos, size))
350                 return -EFAULT;
351
352         *f_pos += size;
353         return size;
354 }
355
356 static ssize_t
357 pdumpfs_mode_write(struct file *filp, const char __user *buf, size_t size,
358                    loff_t *f_pos)
359 {
360         static char tmp[16];
361         int i;
362
363         if (*f_pos > sizeof(tmp))
364                 return -EINVAL;
365
366         if (size > (sizeof(tmp) - *f_pos))
367                 size = sizeof(tmp) - *f_pos;
368
369         if (copy_from_user(tmp + *f_pos, buf, size))
370                 return -EFAULT;
371
372         *f_pos += size;
373
374         mutex_lock(pdumpfs_mutex);
375
376         for (i = 0; pdumpfs_modes[i].name; i++)
377                 if (!strnicmp(tmp, pdumpfs_modes[i].name,
378                               strlen(pdumpfs_modes[i].name))) {
379                         pdumpfs_mode = pdumpfs_modes[i].mode;
380                         mutex_unlock(pdumpfs_mutex);
381                         return size;
382                 }
383
384         mutex_unlock(pdumpfs_mutex);
385         return -EINVAL;
386 }
387
388 static const struct file_operations pdumpfs_mode_fops = {
389         .owner = THIS_MODULE,
390         .llseek = no_llseek,
391         .read = pdumpfs_mode_read,
392         .write = pdumpfs_mode_write,
393 };
394
395 static ssize_t
396 pdumpfs_modes_possible_read(struct file *filp, char __user *buf, size_t size,
397                             loff_t *f_pos)
398 {
399         unsigned int i, skip = *f_pos, pos = 0;
400
401         for (i = 0; pdumpfs_modes[i].name; i++) {
402                 if (i) { /* space */
403                         if (skip)
404                                 skip--;
405                         else if (size > pos) {
406                                 if (copy_to_user(buf + pos, " ", 1))
407                                         return -EFAULT;
408                                 pos++;
409                         }
410                 }
411
412                 if (size) {
413                         int len = strlen(pdumpfs_modes[i].name);
414
415                         if (skip >= len) {
416                                 skip -= len;
417                         } else if (size > pos) {
418                                 len = min(len - skip, size - pos);
419
420                                 if (copy_to_user(buf + pos,
421                                                  pdumpfs_modes[i].name + skip,
422                                                  len))
423                                         return -EFAULT;
424
425                                 skip = 0;
426                                 pos += len;
427                         }
428                 }
429         }
430
431         *f_pos += pos;
432         return pos;
433 }
434
435 static const struct file_operations pdumpfs_modes_possible_fops = {
436         .owner = THIS_MODULE,
437         .llseek = no_llseek,
438         .read = pdumpfs_modes_possible_read,
439 };
440
441 static ssize_t
442 pdumpfs_frame_count_max_read(struct file *filp, char __user *buf, size_t size,
443                              loff_t *f_pos)
444 {
445         char tmp[16];
446
447         tmp[0] = 0;
448
449         mutex_lock(pdumpfs_mutex);
450         snprintf(tmp, sizeof(tmp), "%d", frame_count_max);
451         mutex_unlock(pdumpfs_mutex);
452
453         if (strlen(tmp) < *f_pos)
454                 return 0;
455
456         if ((strlen(tmp) + 1) < (*f_pos + size))
457                 size = strlen(tmp) + 1 - *f_pos;
458
459         if (copy_to_user(buf, tmp + *f_pos, size))
460                 return -EFAULT;
461
462         *f_pos += size;
463         return size;
464 }
465
466 static ssize_t
467 pdumpfs_frame_count_max_write(struct file *filp, const char __user *buf,
468                               size_t size, loff_t *f_pos)
469 {
470         static char tmp[16];
471         unsigned long result = 0;
472
473         if (*f_pos > sizeof(tmp))
474                 return -EINVAL;
475
476         if (size > (sizeof(tmp) - *f_pos))
477                 size = sizeof(tmp) - *f_pos;
478
479         if (copy_from_user(tmp + *f_pos, buf, size))
480                 return -EFAULT;
481
482         tmp[size] = 0;
483
484         mutex_lock(pdumpfs_mutex);
485
486         if (!strict_strtoul(tmp, 0, &result)) {
487                 if (result > 1024)
488                         result = 1024;
489                 if (!result)
490                         result = 1;
491                 frame_count_max = result;
492         }
493
494         mutex_unlock(pdumpfs_mutex);
495
496         *f_pos += size;
497         return size;
498 }
499
500 static const struct file_operations pdumpfs_frame_count_max_fops = {
501         .owner = THIS_MODULE,
502         .llseek = no_llseek,
503         .read = pdumpfs_frame_count_max_read,
504         .write = pdumpfs_frame_count_max_write,
505 };
506
507 static ssize_t
508 pdumpfs_frame_read_single(struct pdumpfs_frame *frame, char __user *buf,
509                           size_t size, loff_t f_pos)
510 {
511         int page;
512         size_t offset;
513
514         if (f_pos >= frame->offset)
515                 return 0;
516
517         if (size > (frame->offset - f_pos))
518                 size = frame->offset - f_pos;
519
520         page = f_pos / PAGE_SIZE;
521         offset = f_pos % PAGE_SIZE;
522
523         if (size > (PAGE_SIZE - offset))
524                 size = PAGE_SIZE - offset;
525
526         if (copy_to_user(buf, ((u8 *) frame->pages[page]) + offset, size))
527                 return -EFAULT;
528
529         return size;
530 }
531
532 static loff_t
533 pdumpfs_llseek_helper(struct file *filp, loff_t offset, int whence, loff_t max)
534 {
535         loff_t f_pos;
536
537         switch (whence) {
538         case SEEK_SET:
539                 if ((offset > max) || (offset < 0))
540                         f_pos = -EINVAL;
541                 else
542                         f_pos = offset;
543                 break;
544         case SEEK_CUR:
545                 if (((filp->f_pos + offset) > max) ||
546                     ((filp->f_pos + offset) < 0))
547                         f_pos = -EINVAL;
548                 else
549                         f_pos = filp->f_pos + offset;
550                 break;
551         case SEEK_END:
552                 if ((offset > 0) ||
553                     (offset < -max))
554                         f_pos = -EINVAL;
555                 else
556                         f_pos = max + offset;
557                 break;
558         default:
559                 f_pos = -EINVAL;
560                 break;
561         }
562
563         if (f_pos >= 0)
564                 filp->f_pos = f_pos;
565
566         return f_pos;
567 }
568
569 static loff_t
570 pdumpfs_init_llseek(struct file *filp, loff_t offset, int whence)
571 {
572         loff_t f_pos;
573
574         mutex_lock(pdumpfs_mutex);
575
576         f_pos = pdumpfs_llseek_helper(filp, offset, whence, frame_init->offset);
577
578         mutex_unlock(pdumpfs_mutex);
579
580         return f_pos;
581 }
582
583 static ssize_t
584 pdumpfs_init_read(struct file *filp, char __user *buf, size_t size,
585                   loff_t *f_pos)
586 {
587         mutex_lock(pdumpfs_mutex);
588
589         size = pdumpfs_frame_read_single(frame_init,
590                                          buf, size, *f_pos);
591
592         mutex_unlock(pdumpfs_mutex);
593
594         if (size > 0)
595                 *f_pos += size;
596         return size;
597 }
598
599 static const struct file_operations pdumpfs_init_fops = {
600         .owner = THIS_MODULE,
601         .llseek = pdumpfs_init_llseek,
602         .read = pdumpfs_init_read,
603 };
604
605 static struct dentry *pdumpfs_dir;
606
607 static void
608 pdumpfs_file_create(const char *name, mode_t mode,
609                     const struct file_operations *fops)
610 {
611         struct dentry *tmp = NULL;
612
613         tmp = debugfs_create_file(name, mode, pdumpfs_dir, NULL, fops);
614         if (!tmp)
615                 pr_err("%s: failed to create pvr/%s file.\n", __func__, name);
616 }
617
618 static int
619 pdumpfs_fs_init(void)
620 {
621         if (!pvr_debugfs_dir) {
622                 pr_err("%s: debugfs pvr/ directory does not exist.\n",
623                        __func__);
624                 return -ENOENT;
625         }
626
627         pdumpfs_dir = debugfs_create_dir("pdump", pvr_debugfs_dir);
628         if (!pdumpfs_dir) {
629                 pr_err("%s: failed to create top level directory.\n",
630                        __func__);
631                 return -ENOENT;
632         }
633
634         pdumpfs_file_create("mode", S_IRUSR | S_IWUSR,
635                             &pdumpfs_mode_fops);
636         pdumpfs_file_create("modes_possible", S_IRUSR,
637                             &pdumpfs_modes_possible_fops);
638
639         pdumpfs_file_create("frame_count_max", S_IRUSR | S_IWUSR,
640                             &pdumpfs_frame_count_max_fops);
641
642         pdumpfs_file_create("init_frame", S_IRUSR,
643                             &pdumpfs_init_fops);
644
645         return 0;
646 }
647
648 static void
649 pdumpfs_fs_destroy(void)
650 {
651         if (pdumpfs_dir)
652                 debugfs_remove_recursive(pdumpfs_dir);
653 }
654
655 int
656 pdumpfs_init(void)
657 {
658         int ret;
659
660         mutex_init(pdumpfs_mutex);
661
662         ret = frame_new(0, 0);
663         if (ret < 0)
664                 return ret;
665
666         pdumpfs_fs_init();
667
668         return 0;
669 }
670
671 void
672 pdumpfs_cleanup(void)
673 {
674         pdumpfs_fs_destroy();
675
676         frame_destroy_all();
677 }