]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jk/spufs
authorPaul Mackerras <paulus@samba.org>
Wed, 30 Apr 2008 06:53:17 +0000 (16:53 +1000)
committerPaul Mackerras <paulus@samba.org>
Wed, 30 Apr 2008 06:53:17 +0000 (16:53 +1000)
1  2 
arch/powerpc/platforms/cell/spufs/file.c

index 08f44d1971ac42cd952f055c6d9f4c96779c5883,b5c1505cf58ba5c092be8210dcedb8da43444c25..80911a37340063a84d0ffaf045802858988eac17
@@@ -32,6 -32,7 +32,6 @@@
  #include <linux/marker.h>
  
  #include <asm/io.h>
 -#include <asm/semaphore.h>
  #include <asm/spu.h>
  #include <asm/spu_info.h>
  #include <asm/uaccess.h>
@@@ -2386,6 -2387,171 +2386,171 @@@ static const struct file_operations spu
        .release        = single_release,
  };
  
+ static inline int spufs_switch_log_used(struct spu_context *ctx)
+ {
+       return (ctx->switch_log->head - ctx->switch_log->tail) %
+               SWITCH_LOG_BUFSIZE;
+ }
+ static inline int spufs_switch_log_avail(struct spu_context *ctx)
+ {
+       return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx);
+ }
+ static int spufs_switch_log_open(struct inode *inode, struct file *file)
+ {
+       struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
+       /*
+        * We (ab-)use the mapping_lock here because it serves the similar
+        * purpose for synchronizing open/close elsewhere.  Maybe it should
+        * be renamed eventually.
+        */
+       mutex_lock(&ctx->mapping_lock);
+       if (ctx->switch_log) {
+               spin_lock(&ctx->switch_log->lock);
+               ctx->switch_log->head = 0;
+               ctx->switch_log->tail = 0;
+               spin_unlock(&ctx->switch_log->lock);
+       } else {
+               /*
+                * We allocate the switch log data structures on first open.
+                * They will never be free because we assume a context will
+                * be traced until it goes away.
+                */
+               ctx->switch_log = kzalloc(sizeof(struct switch_log) +
+                       SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
+                       GFP_KERNEL);
+               if (!ctx->switch_log)
+                       goto out;
+               spin_lock_init(&ctx->switch_log->lock);
+               init_waitqueue_head(&ctx->switch_log->wait);
+       }
+       mutex_unlock(&ctx->mapping_lock);
+       return 0;
+  out:
+       mutex_unlock(&ctx->mapping_lock);
+       return -ENOMEM;
+ }
+ static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n)
+ {
+       struct switch_log_entry *p;
+       p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE;
+       return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n",
+                       (unsigned int) p->tstamp.tv_sec,
+                       (unsigned int) p->tstamp.tv_nsec,
+                       p->spu_id,
+                       (unsigned int) p->type,
+                       (unsigned int) p->val,
+                       (unsigned long long) p->timebase);
+ }
+ static ssize_t spufs_switch_log_read(struct file *file, char __user *buf,
+                            size_t len, loff_t *ppos)
+ {
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
+       int error = 0, cnt = 0;
+       if (!buf || len < 0)
+               return -EINVAL;
+       while (cnt < len) {
+               char tbuf[128];
+               int width;
+               if (file->f_flags & O_NONBLOCK) {
+                       if (spufs_switch_log_used(ctx) <= 0)
+                               return cnt ? cnt : -EAGAIN;
+               } else {
+                       /* Wait for data in buffer */
+                       error = wait_event_interruptible(ctx->switch_log->wait,
+                                       spufs_switch_log_used(ctx) > 0);
+                       if (error)
+                               break;
+               }
+               spin_lock(&ctx->switch_log->lock);
+               if (ctx->switch_log->head == ctx->switch_log->tail) {
+                       /* multiple readers race? */
+                       spin_unlock(&ctx->switch_log->lock);
+                       continue;
+               }
+               width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
+               if (width < len) {
+                       ctx->switch_log->tail =
+                               (ctx->switch_log->tail + 1) %
+                                SWITCH_LOG_BUFSIZE;
+               }
+               spin_unlock(&ctx->switch_log->lock);
+               /*
+                * If the record is greater than space available return
+                * partial buffer (so far)
+                */
+               if (width >= len)
+                       break;
+               error = copy_to_user(buf + cnt, tbuf, width);
+               if (error)
+                       break;
+               cnt += width;
+       }
+       return cnt == 0 ? error : cnt;
+ }
+ static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait)
+ {
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
+       unsigned int mask = 0;
+       poll_wait(file, &ctx->switch_log->wait, wait);
+       if (spufs_switch_log_used(ctx) > 0)
+               mask |= POLLIN;
+       return mask;
+ }
+ static const struct file_operations spufs_switch_log_fops = {
+       .owner  = THIS_MODULE,
+       .open   = spufs_switch_log_open,
+       .read   = spufs_switch_log_read,
+       .poll   = spufs_switch_log_poll,
+ };
+ void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
+               u32 type, u32 val)
+ {
+       if (!ctx->switch_log)
+               return;
+       spin_lock(&ctx->switch_log->lock);
+       if (spufs_switch_log_avail(ctx) > 1) {
+               struct switch_log_entry *p;
+               p = ctx->switch_log->log + ctx->switch_log->head;
+               ktime_get_ts(&p->tstamp);
+               p->timebase = get_tb();
+               p->spu_id = spu ? spu->number : -1;
+               p->type = type;
+               p->val = val;
+               ctx->switch_log->head =
+                       (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE;
+       }
+       spin_unlock(&ctx->switch_log->lock);
+       wake_up(&ctx->switch_log->wait);
+ }
  
  struct tree_descr spufs_dir_contents[] = {
        { "capabilities", &spufs_caps_fops, 0444, },
        { "proxydma_info", &spufs_proxydma_info_fops, 0444, },
        { "tid", &spufs_tid_fops, 0444, },
        { "stat", &spufs_stat_fops, 0444, },
+       { "switch_log", &spufs_switch_log_fops, 0444 },
        {},
  };