[POWERPC] spufs: make mailbox functions handle multiple elements
authorArnd Bergmann <arnd@arndb.de>
Wed, 4 Oct 2006 15:26:17 +0000 (17:26 +0200)
committerPaul Mackerras <paulus@samba.org>
Wed, 4 Oct 2006 23:21:01 +0000 (09:21 +1000)
Since libspe2 will provide a function that can read/write
multiple mailbox elements at once, the kernel should handle
that efficiently.

read/write on the three mailbox files can now access the
spe context multiple times to operate on any number of
mailbox data elements.

If the spu application keeps writing to its outbound
mailbox, the read call will pick up all the data in a
single system call.

Unfortunately, if the user passes an invalid pointer,
we may lose a mailbox element on read, since we can't
put it back. This probably impossible to solve, if the
user also accesses the mailbox through direct register
access.

Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/platforms/cell/spufs/file.c

index 514a1d508035ac24dfaee36a96e162b2a061814f..b4d38cb65f17ba332c699b865cf34e7092c39de3 100644 (file)
@@ -354,27 +354,54 @@ static int spufs_pipe_open(struct inode *inode, struct file *file)
        return nonseekable_open(inode, file);
 }
 
+/*
+ * Read as many bytes from the mailbox as possible, until
+ * one of the conditions becomes true:
+ *
+ * - no more data available in the mailbox
+ * - end of the user provided buffer
+ * - end of the mapped area
+ */
 static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
                        size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       u32 mbox_data;
-       int ret;
+       u32 mbox_data, __user *udata;
+       ssize_t count;
 
        if (len < 4)
                return -EINVAL;
 
+       if (!access_ok(VERIFY_WRITE, buf, len))
+               return -EFAULT;
+
+       udata = (void __user *)buf;
+
        spu_acquire(ctx);
-       ret = ctx->ops->mbox_read(ctx, &mbox_data);
+       for (count = 0; count <= len; count += 4, udata++) {
+               int ret;
+               ret = ctx->ops->mbox_read(ctx, &mbox_data);
+               if (ret == 0)
+                       break;
+
+               /*
+                * at the end of the mapped area, we can fault
+                * but still need to return the data we have
+                * read successfully so far.
+                */
+               ret = __put_user(mbox_data, udata);
+               if (ret) {
+                       if (!count)
+                               count = -EFAULT;
+                       break;
+               }
+       }
        spu_release(ctx);
 
-       if (!ret)
-               return -EAGAIN;
-
-       if (copy_to_user(buf, &mbox_data, sizeof mbox_data))
-               return -EFAULT;
+       if (!count)
+               count = -EAGAIN;
 
-       return 4;
+       return count;
 }
 
 static struct file_operations spufs_mbox_fops = {
@@ -430,36 +457,70 @@ void spufs_ibox_callback(struct spu *spu)
        kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN);
 }
 
+/*
+ * Read as many bytes from the interrupt mailbox as possible, until
+ * one of the conditions becomes true:
+ *
+ * - no more data available in the mailbox
+ * - end of the user provided buffer
+ * - end of the mapped area
+ *
+ * If the file is opened without O_NONBLOCK, we wait here until
+ * any data is available, but return when we have been able to
+ * read something.
+ */
 static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
                        size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       u32 ibox_data;
-       ssize_t ret;
+       u32 ibox_data, __user *udata;
+       ssize_t count;
 
        if (len < 4)
                return -EINVAL;
 
+       if (!access_ok(VERIFY_WRITE, buf, len))
+               return -EFAULT;
+
+       udata = (void __user *)buf;
+
        spu_acquire(ctx);
 
-       ret = 0;
+       /* wait only for the first element */
+       count = 0;
        if (file->f_flags & O_NONBLOCK) {
                if (!spu_ibox_read(ctx, &ibox_data))
-                       ret = -EAGAIN;
+                       count = -EAGAIN;
        } else {
-               ret = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
+               count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
        }
+       if (count)
+               goto out;
 
-       spu_release(ctx);
+       /* if we can't write at all, return -EFAULT */
+       count = __put_user(ibox_data, udata);
+       if (count)
+               goto out;
 
-       if (ret)
-               return ret;
+       for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
+               int ret;
+               ret = ctx->ops->ibox_read(ctx, &ibox_data);
+               if (ret == 0)
+                       break;
+               /*
+                * at the end of the mapped area, we can fault
+                * but still need to return the data we have
+                * read successfully so far.
+                */
+               ret = __put_user(ibox_data, udata);
+               if (ret)
+                       break;
+       }
 
-       ret = 4;
-       if (copy_to_user(buf, &ibox_data, sizeof ibox_data))
-               ret = -EFAULT;
+out:
+       spu_release(ctx);
 
-       return ret;
+       return count;
 }
 
 static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait)
@@ -532,32 +593,67 @@ void spufs_wbox_callback(struct spu *spu)
        kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT);
 }
 
+/*
+ * Write as many bytes to the interrupt mailbox as possible, until
+ * one of the conditions becomes true:
+ *
+ * - the mailbox is full
+ * - end of the user provided buffer
+ * - end of the mapped area
+ *
+ * If the file is opened without O_NONBLOCK, we wait here until
+ * space is availabyl, but return when we have been able to
+ * write something.
+ */
 static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
                        size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       u32 wbox_data;
-       int ret;
+       u32 wbox_data, __user *udata;
+       ssize_t count;
 
        if (len < 4)
                return -EINVAL;
 
-       if (copy_from_user(&wbox_data, buf, sizeof wbox_data))
+       udata = (void __user *)buf;
+       if (!access_ok(VERIFY_READ, buf, len))
+               return -EFAULT;
+
+       if (__get_user(wbox_data, udata))
                return -EFAULT;
 
        spu_acquire(ctx);
 
-       ret = 0;
+       /*
+        * make sure we can at least write one element, by waiting
+        * in case of !O_NONBLOCK
+        */
+       count = 0;
        if (file->f_flags & O_NONBLOCK) {
                if (!spu_wbox_write(ctx, wbox_data))
-                       ret = -EAGAIN;
+                       count = -EAGAIN;
        } else {
-               ret = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
+               count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
        }
 
-       spu_release(ctx);
+       if (count)
+               goto out;
 
-       return ret ? ret : sizeof wbox_data;
+       /* write aŃ• much as possible */
+       for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
+               int ret;
+               ret = __get_user(wbox_data, udata);
+               if (ret)
+                       break;
+
+               ret = spu_wbox_write(ctx, wbox_data);
+               if (ret == 0)
+                       break;
+       }
+
+out:
+       spu_release(ctx);
+       return count;
 }
 
 static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait)