+ return read_pos;
+}
+
+/**
+ * relay_file_read_end_pos - return the new read position
+ */
+static size_t relay_file_read_end_pos(struct rchan_buf *buf,
+ size_t read_pos,
+ size_t count)
+{
+ size_t read_subbuf, padding, end_pos;
+ size_t subbuf_size = buf->chan->subbuf_size;
+ size_t n_subbufs = buf->chan->n_subbufs;
+
+ read_subbuf = read_pos / subbuf_size;
+ padding = buf->padding[read_subbuf];
+ if (read_pos % subbuf_size + count + padding == subbuf_size)
+ end_pos = (read_subbuf + 1) * subbuf_size;
+ else
+ end_pos = read_pos + count;
+ if (end_pos >= subbuf_size * n_subbufs)
+ end_pos = 0;
+
+ return end_pos;
+}
+
+/**
+ * relay_file_read - read file op for relay files
+ * @filp: the file
+ * @buffer: the userspace buffer
+ * @count: number of bytes to read
+ * @ppos: position to read from
+ *
+ * Reads count bytes or the number of bytes available in the
+ * current sub-buffer being read, whichever is smaller.
+ */
+static ssize_t relay_file_read(struct file *filp,
+ char __user *buffer,
+ size_t count,
+ loff_t *ppos)
+{
+ struct rchan_buf *buf = filp->private_data;
+ struct inode *inode = filp->f_dentry->d_inode;
+ size_t read_start, avail;
+ ssize_t ret = 0;
+ void *from;
+
+ mutex_lock(&inode->i_mutex);
+ if(!relay_file_read_avail(buf, *ppos))
+ goto out;
+
+ read_start = relay_file_read_start_pos(*ppos, buf);
+ avail = relay_file_read_subbuf_avail(read_start, buf);
+ if (!avail)
+ goto out;
+
+ from = buf->start + read_start;
+ ret = count = min(count, avail);
+ if (copy_to_user(buffer, from, count)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ relay_file_read_consume(buf, read_start, count);
+ *ppos = relay_file_read_end_pos(buf, read_start, count);
+out:
+ mutex_unlock(&inode->i_mutex);
+ return ret;
+}