diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/relay.c | 175 |
1 files changed, 91 insertions, 84 deletions
diff --git a/kernel/relay.c b/kernel/relay.c index fefe2b2a7277..33345e73485c 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -866,131 +866,138 @@ static size_t relay_file_read_end_pos(struct rchan_buf *buf, } /** - * 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. + * subbuf_read_actor - read up to one subbuf's worth of data */ -static ssize_t relay_file_read(struct file *filp, - char __user *buffer, - size_t count, - loff_t *ppos) +static int subbuf_read_actor(size_t read_start, + struct rchan_buf *buf, + size_t avail, + read_descriptor_t *desc, + read_actor_t actor) { - 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; + int ret = 0; from = buf->start + read_start; - ret = count = min(count, avail); - if (copy_to_user(buffer, from, count)) { - ret = -EFAULT; - goto out; + ret = avail; + if (copy_to_user(desc->arg.data, from, avail)) { + desc->error = -EFAULT; + ret = 0; } - relay_file_read_consume(buf, read_start, count); - *ppos = relay_file_read_end_pos(buf, read_start, count); -out: - mutex_unlock(&inode->i_mutex); + desc->arg.data += ret; + desc->written += ret; + desc->count -= ret; + return ret; } -static ssize_t relay_file_sendsubbuf(struct file *filp, loff_t *ppos, - size_t count, read_actor_t actor, - void *target) +/** + * subbuf_send_actor - send up to one subbuf's worth of data + */ +static int subbuf_send_actor(size_t read_start, + struct rchan_buf *buf, + size_t avail, + read_descriptor_t *desc, + read_actor_t actor) { - struct rchan_buf *buf = filp->private_data; - read_descriptor_t desc; - size_t read_start, avail; unsigned long pidx, poff; unsigned int subbuf_pages; - ssize_t ret = 0; - - if (!relay_file_read_avail(buf, *ppos)) - return 0; - - read_start = relay_file_read_start_pos(*ppos, buf); - avail = relay_file_read_subbuf_avail(read_start, buf); - if (!avail) - return 0; - - count = min(count, avail); - - desc.written = 0; - desc.count = count; - desc.arg.data = target; - desc.error = 0; + int ret = 0; subbuf_pages = buf->chan->alloc_size >> PAGE_SHIFT; pidx = (read_start / PAGE_SIZE) % subbuf_pages; poff = read_start & ~PAGE_MASK; - while (count) { + while (avail) { struct page *p = buf->page_array[pidx]; unsigned int len; len = PAGE_SIZE - poff; - if (len > count) - len = count; + if (len > avail) + len = avail; - len = actor(&desc, p, poff, len); - - if (desc.error) { - if (!ret) - ret = desc.error; + len = actor(desc, p, poff, len); + if (desc->error) break; - } - count -= len; + avail -= len; ret += len; poff = 0; pidx = (pidx + 1) % subbuf_pages; } - if (ret > 0) { - relay_file_read_consume(buf, read_start, ret); - *ppos = relay_file_read_end_pos(buf, read_start, ret); - } - return ret; } -static ssize_t relay_file_sendfile(struct file *filp, loff_t *ppos, - size_t count, read_actor_t actor, - void *target) +typedef int (*subbuf_actor_t) (size_t read_start, + struct rchan_buf *buf, + size_t avail, + read_descriptor_t *desc, + read_actor_t actor); + +/** + * relay_file_read_subbufs - read count bytes, bridging subbuf boundaries + */ +static inline ssize_t relay_file_read_subbufs(struct file *filp, + loff_t *ppos, + size_t count, + subbuf_actor_t subbuf_actor, + read_actor_t actor, + void *target) { - ssize_t sent = 0, ret = 0; + struct rchan_buf *buf = filp->private_data; + size_t read_start, avail; + read_descriptor_t desc; + int ret; if (!count) return 0; - mutex_lock(&filp->f_dentry->d_inode->i_mutex); + desc.written = 0; + desc.count = count; + desc.arg.data = target; + desc.error = 0; + mutex_lock(&filp->f_dentry->d_inode->i_mutex); do { - ret = relay_file_sendsubbuf(filp, ppos, count, actor, target); - if (ret < 0) { - if (!sent) - sent = ret; + if (!relay_file_read_avail(buf, *ppos)) + break; + + read_start = relay_file_read_start_pos(*ppos, buf); + avail = relay_file_read_subbuf_avail(read_start, buf); + if (!avail) break; - } - count -= ret; - sent += ret; - } while (count && ret); + avail = min(desc.count, avail); + ret = subbuf_actor(read_start, buf, avail, &desc, actor); + if (desc.error < 0) + break; + + if (ret) { + relay_file_read_consume(buf, read_start, ret); + *ppos = relay_file_read_end_pos(buf, read_start, ret); + } + } while (desc.count && ret); mutex_unlock(&filp->f_dentry->d_inode->i_mutex); - return sent; + + return desc.written; +} + +static ssize_t relay_file_read(struct file *filp, + char __user *buffer, + size_t count, + loff_t *ppos) +{ + return relay_file_read_subbufs(filp, ppos, count, subbuf_read_actor, + NULL, buffer); +} + +static ssize_t relay_file_sendfile(struct file *filp, + loff_t *ppos, + size_t count, + read_actor_t actor, + void *target) +{ + return relay_file_read_subbufs(filp, ppos, count, subbuf_send_actor, + actor, target); } struct file_operations relay_file_operations = { |