gpu: pvr: pdumpfs: fix for imgtec simulator
[sgx.git] / pvr / pvr_pdumpfs.c
index 60cb726..6050fd3 100644 (file)
@@ -56,6 +56,7 @@ struct pdumpfs_frame {
        unsigned long pages[FRAME_PAGE_COUNT];
 };
 
+#define MAX_FRAME_COUNT_HARD 1024
 static u32 frame_count_max = CONFIG_PVR_PDUMP_INITIAL_MAX_FRAME_COUNT;
 static u32 frame_count;
 
@@ -63,6 +64,14 @@ static struct pdumpfs_frame *frame_init;
 static struct pdumpfs_frame *frame_stream;
 static struct pdumpfs_frame *frame_current;
 
+static struct pdumpfs_frame *frame_current_debugfs;
+static int frame_current_open_count;
+static int frame_stream_open_count;
+
+static loff_t stream_start;
+static loff_t stream_f_pos;
+static loff_t stream_end;
+
 static struct pdumpfs_frame *
 frame_create(void)
 {
@@ -93,11 +102,21 @@ frame_destroy(struct pdumpfs_frame *frame)
 static void
 frame_destroy_all(void)
 {
+       /* detach from possible clones */
+       if (frame_current_debugfs &&
+           ((frame_current_debugfs == frame_init) ||
+            (frame_current_debugfs == frame_current) ||
+            frame_current_debugfs->next))
+               frame_current_debugfs = NULL;
+
        frame_current = NULL;
 
        frame_destroy(frame_init);
        frame_init = NULL;
 
+       frame_destroy(frame_current_debugfs);
+       frame_current_debugfs = NULL;
+
        while (frame_stream) {
                struct pdumpfs_frame *frame = frame_stream;
 
@@ -106,19 +125,46 @@ frame_destroy_all(void)
                frame_destroy(frame);
                frame_count--;
        }
+
+       stream_start = 0;
+       stream_f_pos = 0;
+       stream_end = 0;
 }
 
 static void
-frame_cull(void)
+frame_cull_first(void)
 {
-       while (frame_count > frame_count_max) {
-               struct pdumpfs_frame *frame = frame_stream;
+       struct pdumpfs_frame *frame = frame_stream;
 
-               frame_stream = frame->next;
-               frame->next = NULL;
+       frame_stream = frame->next;
+       frame->next = NULL;
 
+       if (frame_stream_open_count)
+               stream_start += frame->offset;
+       else
+               stream_end -= frame->offset;
+
+       /*
+        * we cannot have frames vanish in the middle of reading
+        * them through debugfs
+        */
+       if (frame != frame_current_debugfs)
                frame_destroy(frame);
-               frame_count--;
+
+       frame_count--;
+}
+
+static void
+frame_cull(void)
+{
+
+       if (!frame_stream_open_count) {
+               while (frame_count > frame_count_max)
+                       frame_cull_first();
+       } else {
+               while (((stream_start + frame_stream->offset) < stream_f_pos) ||
+                      (frame_count > MAX_FRAME_COUNT_HARD))
+                       frame_cull_first();
        }
 }
 
@@ -169,7 +215,8 @@ pdumpfs_capture_enabled(void)
 
        mutex_lock(pdumpfs_mutex);
 
-       if (pdumpfs_mode == PDUMPFS_MODE_FULL)
+       if ((pdumpfs_mode == PDUMPFS_MODE_FULL) &&
+           (frame_current != frame_init)) /* simulator bails otherwise */
                ret = true;
        else
                ret = false;
@@ -189,10 +236,12 @@ pdumpfs_flags_check(u32 flags)
 
        mutex_lock(pdumpfs_mutex);
 
-       if (pdumpfs_mode == PDUMPFS_MODE_FULL)
+       if (pdumpfs_mode == PDUMPFS_MODE_DISABLED)
+               ret = false;
+       else if ((pdumpfs_mode == PDUMPFS_MODE_FULL) &&
+                (frame_current != frame_init)) /* simulator bails otherwise */
                ret = true;
-       else if ((pdumpfs_mode == PDUMPFS_MODE_STANDARD) &&
-                (flags & PDUMP_FLAGS_CONTINUOUS))
+       else if (flags & PDUMP_FLAGS_CONTINUOUS)
                ret = true;
        else
                ret = false;
@@ -290,6 +339,8 @@ pdumpfs_write_data(void *buffer, size_t size, bool from_user)
        mutex_lock(pdumpfs_mutex);
 
        size = pdumpfs_frame_write(buffer, size, from_user);
+       if ((size > 0) && (frame_current != frame_init))
+               stream_end += size;
 
        mutex_unlock(pdumpfs_mutex);
 
@@ -302,9 +353,13 @@ pdumpfs_write_data(void *buffer, size_t size, bool from_user)
 void
 pdumpfs_write_string(char *string)
 {
+       size_t size;
+
        mutex_lock(pdumpfs_mutex);
 
-       pdumpfs_frame_write(string, strlen(string), false);
+       size = pdumpfs_frame_write(string, strlen(string), false);
+       if ((size > 0) && (frame_current != frame_init))
+               stream_end += size;
 
        mutex_unlock(pdumpfs_mutex);
 }
@@ -602,6 +657,253 @@ static const struct file_operations pdumpfs_init_fops = {
        .read = pdumpfs_init_read,
 };
 
+/*
+ * We need to make sure that our frame doesn't vanish while we are still
+ * reading it: so reference this frame again.
+ */
+static int
+pdumpfs_current_open(struct inode *inode, struct file *filp)
+{
+       mutex_lock(pdumpfs_mutex);
+
+       if (!frame_current_open_count)
+               frame_current_debugfs = frame_current;
+
+       frame_current_open_count++;
+
+       mutex_unlock(pdumpfs_mutex);
+       return 0;
+}
+
+static int
+pdumpfs_current_release(struct inode *inode, struct file *filp)
+{
+       mutex_lock(pdumpfs_mutex);
+
+       frame_current_open_count--;
+
+       if (!frame_current_open_count) {
+               if ((frame_current_debugfs != frame_init) &&
+                   (frame_current_debugfs != frame_current) &&
+                   !frame_current_debugfs->next)
+                       frame_destroy(frame_current_debugfs);
+               frame_current_debugfs = NULL;
+       }
+
+       mutex_unlock(pdumpfs_mutex);
+       return 0;
+}
+
+static loff_t
+pdumpfs_current_llseek(struct file *filp, loff_t offset, int whence)
+{
+       loff_t f_pos;
+
+       mutex_lock(pdumpfs_mutex);
+
+       f_pos = pdumpfs_llseek_helper(filp, offset, whence,
+                                     frame_current_debugfs->offset);
+
+       mutex_unlock(pdumpfs_mutex);
+
+       return f_pos;
+}
+
+static ssize_t
+pdumpfs_current_read(struct file *filp, char __user *buf, size_t size,
+                    loff_t *f_pos)
+{
+       mutex_lock(pdumpfs_mutex);
+
+       if (frame_current_debugfs->offset)
+               size = pdumpfs_frame_read_single(frame_current_debugfs,
+                                                buf, size, *f_pos);
+       else
+               size = 0;
+
+       mutex_unlock(pdumpfs_mutex);
+
+       if (size > 0)
+               *f_pos += size;
+       return size;
+}
+
+static const struct file_operations pdumpfs_current_fops = {
+       .owner = THIS_MODULE,
+       .llseek = pdumpfs_current_llseek,
+       .read = pdumpfs_current_read,
+       .open = pdumpfs_current_open,
+       .release = pdumpfs_current_release,
+};
+
+/*
+ * So we can track when we can alter stream offsets.
+ */
+static int
+pdumpfs_stream_open(struct inode *inode, struct file *filp)
+{
+       int ret;
+
+       mutex_lock(pdumpfs_mutex);
+
+       if (frame_stream_open_count)
+               ret = -EUSERS;
+       else {
+               frame_stream_open_count++;
+               ret = 0;
+       }
+
+       mutex_unlock(pdumpfs_mutex);
+       return ret;
+}
+
+static int
+pdumpfs_stream_release(struct inode *inode, struct file *filp)
+{
+       mutex_lock(pdumpfs_mutex);
+
+       frame_stream_open_count--;
+
+       /* fix the damage done while it was open */
+       if (!frame_stream_open_count) {
+               stream_end -= stream_start;
+               stream_start = 0;
+               stream_f_pos = 0;
+       }
+
+       mutex_unlock(pdumpfs_mutex);
+       return 0;
+}
+
+static ssize_t
+pdumpfs_stream_buffer_clear(char __user *buf, size_t size)
+{
+       char tmp[80];
+
+       memset(tmp, '.', sizeof(tmp) - 1);
+       tmp[sizeof(tmp) - 1] = '\n';
+
+       if (size >= sizeof(tmp)) {
+               int i;
+
+               for (i = 0; (i  + sizeof(tmp)) < size; i += sizeof(tmp))
+                       if (copy_to_user(buf + i, tmp, sizeof(tmp)))
+                               return -EFAULT;
+               return i;
+       } else {
+               if (copy_to_user(buf, tmp + sizeof(tmp) - size, size))
+                       return -EFAULT;
+               return size;
+       }
+}
+
+static ssize_t
+pdumpfs_stream_buffer_fill(struct pdumpfs_frame *frame,
+                          char __user *buf, size_t offset, size_t size)
+{
+       int page = offset / PAGE_SIZE;
+
+       if (size > (frame->offset - offset))
+               size = frame->offset - offset;
+
+       offset %= PAGE_SIZE;
+
+       if (size > (PAGE_SIZE - offset))
+               size = PAGE_SIZE - offset;
+
+       if (copy_to_user(buf, ((u8 *) frame->pages[page]) + offset, size))
+               return -EFAULT;
+
+       stream_f_pos += size;
+
+       return size;
+}
+
+static loff_t
+pdumpfs_stream_llseek(struct file *filp, loff_t offset, int whence)
+{
+       loff_t f_pos;
+
+       mutex_lock(pdumpfs_mutex);
+
+       switch (whence) {
+       case SEEK_SET:
+               if ((offset > stream_end) || (offset < stream_start))
+                       f_pos = -EINVAL;
+               else
+                       f_pos = offset;
+               break;
+       case SEEK_CUR:
+               if (((filp->f_pos + offset) > stream_end) ||
+                   ((filp->f_pos + offset) < stream_start))
+                       f_pos = -EINVAL;
+               else
+                       f_pos = filp->f_pos + offset;
+               break;
+       case SEEK_END:
+               if ((offset > 0) ||
+                   (offset < (stream_start - stream_end)))
+                       f_pos = -EINVAL;
+               else
+                       f_pos = stream_end + offset;
+               break;
+       default:
+               f_pos = -EINVAL;
+               break;
+       }
+
+       if (f_pos >= 0) {
+               filp->f_pos = f_pos;
+               stream_f_pos = f_pos;
+       }
+
+       mutex_unlock(pdumpfs_mutex);
+
+       return f_pos;
+}
+
+static ssize_t
+pdumpfs_stream_read(struct file *filp, char __user *buf, size_t size,
+                   loff_t *f_pos)
+{
+       size_t ret = 0;
+
+       mutex_lock(pdumpfs_mutex);
+
+       if ((stream_end <= 0) || (*f_pos >= stream_end))
+               ret = 0;
+       else if (*f_pos < stream_start) {
+               if (size > (stream_start - *f_pos))
+                       size = stream_start - *f_pos;
+               ret = pdumpfs_stream_buffer_clear(buf, size);
+       } else {
+               loff_t start = stream_start;
+               struct pdumpfs_frame *frame = frame_stream;
+
+               /* skip frames that are before our offset */
+               while ((start + frame->offset) <= *f_pos) {
+                       start += frame->offset;
+                       frame = frame->next;
+               }
+
+               ret = pdumpfs_stream_buffer_fill(frame, buf, *f_pos - start,
+                                                size);
+       }
+
+       if (ret > 0)
+               *f_pos += ret;
+       mutex_unlock(pdumpfs_mutex);
+       return ret;
+}
+
+static const struct file_operations pdumpfs_stream_fops = {
+       .owner = THIS_MODULE,
+       .llseek = pdumpfs_stream_llseek,
+       .read = pdumpfs_stream_read,
+       .open = pdumpfs_stream_open,
+       .release = pdumpfs_stream_release,
+};
+
 static struct dentry *pdumpfs_dir;
 
 static void
@@ -641,6 +943,10 @@ pdumpfs_fs_init(void)
 
        pdumpfs_file_create("init_frame", S_IRUSR,
                            &pdumpfs_init_fops);
+       pdumpfs_file_create("current_frame", S_IRUSR,
+                           &pdumpfs_current_fops);
+       pdumpfs_file_create("stream_frames", S_IRUSR,
+                           &pdumpfs_stream_fops);
 
        return 0;
 }