[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Minios-devel] [UNIKRAFT PATCH] lib/vfscore: Add anonymous pipe



Hi Bogdan

Thanks for this patch, we certainly need the pipe primitive for
Unikraft. However, this patch needs a bit of rework. First of all, it
should also compile using nolibc, but it fails now because 'sys/ioctl.h'
is missing from nolibc.

For the rest of the comments, please see inline.

On 6/29/19 1:03 PM, Bogdan Lascu wrote:
> Adds pipe function that creates an anonymous pipe and creates functions
> that write and read to a pipe file descriptor.
> 
> Signed-off-by: Bogdan Lascu <lascu.bogdan96@xxxxxxxxx>
> ---
>  lib/vfscore/Makefile.uk            |   1 +
>  lib/vfscore/exportsyms.uk          |   1 +
>  lib/vfscore/include/vfscore/pipe.h |  67 ++++++
>  lib/vfscore/pipe.c                 | 409 
> +++++++++++++++++++++++++++++++++++++
>  4 files changed, 478 insertions(+)
>  create mode 100644 lib/vfscore/include/vfscore/pipe.h
>  create mode 100644 lib/vfscore/pipe.c
> 
> diff --git a/lib/vfscore/Makefile.uk b/lib/vfscore/Makefile.uk
> index 0166e612..4e519367 100644
> --- a/lib/vfscore/Makefile.uk
> +++ b/lib/vfscore/Makefile.uk
> @@ -14,6 +14,7 @@ LIBVFSCORE_SRCS-y += $(LIBVFSCORE_BASE)/task.c
>  LIBVFSCORE_SRCS-y += $(LIBVFSCORE_BASE)/lookup.c
>  LIBVFSCORE_SRCS-y += $(LIBVFSCORE_BASE)/fops.c
>  LIBVFSCORE_SRCS-y += $(LIBVFSCORE_BASE)/subr_uio.c
> +LIBVFSCORE_SRCS-y += $(LIBVFSCORE_BASE)/pipe.c
>  
>  EXTRA_LD_SCRIPT-$(CONFIG_LIBVFSCORE) += $(LIBVFSCORE_BASE)/extra.ld
>  
> diff --git a/lib/vfscore/exportsyms.uk b/lib/vfscore/exportsyms.uk
> index da7fbaea..16a0f86a 100644
> --- a/lib/vfscore/exportsyms.uk
> +++ b/lib/vfscore/exportsyms.uk
> @@ -57,6 +57,7 @@ writev
>  truncate
>  mknod
>  preadv
> +pipe
>  ioctl
>  fdatasync
>  fdopendir
> diff --git a/lib/vfscore/include/vfscore/pipe.h 
> b/lib/vfscore/include/vfscore/pipe.h
> new file mode 100644
> index 00000000..0c25f4ed
> --- /dev/null
> +++ b/lib/vfscore/include/vfscore/pipe.h
> @@ -0,0 +1,67 @@
> +/* pipe.h - pipe header
> + *
> + * Authors: Bogdan-George Lascu <lascu.bogdan96@xxxxxxxxx>
> + *
> + * Copyright (c) 2019, University Politehnica of Bucharest. All rights 
> reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + * 3. Neither the name of the copyright holder nor the names of its
> + *    contributors may be used to endorse or promote products derived from
> + *    this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
> IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + *
> + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
> + */
> +#include <vfscore/file.h>
> +#include <uk/wait.h>
> +#include <uk/mutex.h>
> +
> +#define PIPE_MAX_SIZE        (1 << 16)
> +
> +
> +struct pipe_buf {
> +     char *data;
> +     unsigned int max_size;
> +     size_t len;     /* Total len of buffer. */
> +     unsigned int r_off;
> +     unsigned int w_off;
> +};
> +
> +struct pipe_info {
> +     struct pipe_buf *buf;
> +     int w_count;
> +     int r_count;
> +     int flags;
> +     struct uk_mutex pipe_lock;
> +     struct uk_waitq wq;
> +};

It would help to add some comments for each structure and for each of
the structure fields.

> +
> +struct pipe_buf *alloc_pipe_buf(int size);
> +struct pipe_info *alloc_pipe_info(int size, int flags);
> +
> +void free_pipe_buf(struct pipe_buf *pipe_buf);
> +void free_pipe_info(struct pipe_info *pipe_info);

The same applies for these functions.

> +
> +
> +#define PIPE_LOCK(__pipe)    uk_mutex_lock(&__pipe->pipe_lock)
> +#define PIPE_UNLOCK(__pipe)  uk_mutex_unlock(&__pipe->pipe_lock)

I fail to understand why we use this header with all these declarations
considering that the only public function of the pipe implementation is
the POSIX pipe().

> +
> diff --git a/lib/vfscore/pipe.c b/lib/vfscore/pipe.c
> new file mode 100644
> index 00000000..15137627
> --- /dev/null
> +++ b/lib/vfscore/pipe.c
> @@ -0,0 +1,409 @@
> +/* pipe.c - pipe implementation
> + *
> + * Authors: Bogdan-George Lascu <lascu.bogdan96@xxxxxxxxx>
> + *
> + * Copyright (c) 2019, University Politehnica of Bucharest. All rights 
> reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + * 3. Neither the name of the copyright holder nor the names of its
> + *    contributors may be used to endorse or promote products derived from
> + *    this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
> IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + *
> + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <vfscore/pipe.h>
> +#include <vfscore/file.h>
> +#include <vfscore/vnode.h>
> +#include <vfscore/fs.h>
> +#include <uk/wait.h>
> +#include <sys/ioctl.h>
> +
> +
> +size_t get_pipe_buf_free_space(struct pipe_buf *pipe_buf)
> +{
> +     return pipe_buf->max_size - pipe_buf->len;
> +}
> +
> +int pipe_can_read(struct pipe_buf *pipe_buf)
> +{
> +     return pipe_buf->len > 0;
> +}
> +
> +/* Write blocks until there is enough space to write the entire user buffer. 
> */
> +int pipe_can_write(struct pipe_buf *pipe_buf, size_t user_buf_len)
> +{
> +     return get_pipe_buf_free_space(pipe_buf) >= user_buf_len;
> +}
> +
> +
> +static int pipe_write(struct vnode *vnode,
> +                           struct uio *buf, int ioflag __unused)
> +{
> +     struct pipe_info *pipe = vnode->v_data;
> +     struct pipe_buf *pipe_buf = pipe->buf;
> +     struct iovec iovec;
> +     int to_write = 0;
> +     int i;
> +
> +     if (!pipe->r_count) {
> +             // TODO before returning the error, send a SIGPIPE signal.
> +             return -EPIPE;
> +     }
> +
> +     for (i = 0; i < buf->uio_iovcnt; i++)
> +             to_write += buf->uio_iov[i].iov_len;
> +
> +     uk_waitq_wait_event(&pipe->wq, pipe_can_write(pipe_buf, to_write));
> +     PIPE_LOCK(pipe);
> +     for (i = 0; i < buf->uio_iovcnt; i++) {
> +             iovec = buf->uio_iov[i];
> +
> +             /* Copy the user buffer in pipe buffer. */
> +             if (pipe_buf->w_off + iovec.iov_len <= pipe_buf->max_size) {
> +                     memcpy(pipe_buf->data + pipe_buf->w_off,
> +                            iovec.iov_base,
> +                            iovec.iov_len);
> +             } else {
> +                     int first_copy_bytes;
> +                     int second_copy_bytes;
> +
> +                     /* Copy the from current offset to the end. */
> +                     first_copy_bytes = pipe_buf->max_size - pipe_buf->w_off;
> +                     memcpy(pipe_buf->data + pipe_buf->w_off,
> +                            iovec.iov_base,
> +                            first_copy_bytes);
> +
> +                     /* Copy from the begging the remaining bytes. */
> +                     second_copy_bytes = pipe_buf->w_off
> +                                         + iovec.iov_len
> +                                         - pipe_buf->max_size;
> +
> +                     memcpy(pipe_buf->data,
> +                            iovec.iov_base + first_copy_bytes,
> +                            second_copy_bytes);
> +
> +             }
> +
> +             /* Update buffer write offset. */
> +             pipe_buf->w_off = (pipe_buf->w_off + iovec.iov_len)
> +                               % pipe_buf->max_size;
> +
> +             /* Update pipe buf len. */
> +             pipe_buf->len += iovec.iov_len;
> +
> +             /* Update bytes written. */
> +             buf->uio_resid -= iovec.iov_len;
> +     }
> +     PIPE_UNLOCK(pipe);
> +     uk_waitq_wake_up(&pipe->wq);
> +
> +     return 0;
> +}
> +
> +static int pipe_read(struct vnode *vnode,
> +                          struct vfscore_file *vfscore_file __unused,
> +                          struct uio *buf, int ioflag __unused)
> +{
> +     struct pipe_info *pipe = vnode->v_data;
> +     struct pipe_buf *pipe_buf = pipe->buf;
> +     struct iovec iovec;
> +     int to_read;
> +     int i;
> +
> +     if ((vfscore_file->f_flags & O_NONBLOCK) && pipe_buf->len == 0)
> +             return -EAGAIN;
> +
> +     uk_waitq_wait_event(&pipe->wq, pipe_can_read(pipe_buf));
> +     PIPE_LOCK(pipe);
> +     for (i = 0; i < buf->uio_iovcnt; i++) {
> +             iovec = buf->uio_iov[i];
> +             to_read = MIN(iovec.iov_len, pipe_buf->len);
> +             if (to_read == 0)
> +                     break;
> +
> +             /* Copy from pipe buffer to user buffer. */
> +             if (pipe_buf->r_off + to_read <= pipe_buf->max_size) {
> +                     memcpy(iovec.iov_base,
> +                            pipe_buf->data + pipe_buf->r_off,
> +                            to_read);
> +             } else {
> +                     int first_copy_bytes;
> +                     int second_copy_bytes;
> +
> +                     /* Copy the from current offset to the end. */
> +                     first_copy_bytes = pipe_buf->max_size - pipe_buf->r_off;
> +                     memcpy(iovec.iov_base,
> +                            pipe_buf->data + pipe_buf->r_off,
> +                            first_copy_bytes);
> +
> +                     /* Copy from the begging the remaining bytes. */
> +                     second_copy_bytes = pipe_buf->r_off
> +                                         + to_read
> +                                         - pipe_buf->max_size;
> +
> +                     memcpy(iovec.iov_base + first_copy_bytes,
> +                            pipe_buf->data,
> +                            second_copy_bytes);
> +
> +             }
> +
> +             /* Update read buffer offset. */
> +             pipe_buf->r_off = (pipe_buf->r_off + to_read) % 
> pipe_buf->max_size;

You have a checkpatch warning here. I think you can put the assigned
value on the next line.

> +
> +             /* Update pipe buffer len. */
> +             pipe_buf->len -= to_read;
> +
> +             /* Update bytes read which will be returned. */
> +             buf->uio_resid -= to_read;
> +     }
> +     PIPE_UNLOCK(pipe);
> +     uk_waitq_wake_up(&pipe->wq);
> +
> +     return 0;
> +}
> +
> +static int pipe_close(struct vnode *vnode,
> +                       struct vfscore_file *file)
> +{
> +     struct pipe_info *pipe = vnode->v_data;
> +
> +     PIPE_LOCK(pipe);
> +
> +     if (file->f_flags & UK_FREAD)
> +             pipe->r_count--;
> +
> +     if (file->f_flags & UK_FWRITE)
> +             pipe->w_count--;
> +
> +     if (!pipe->r_count && !pipe->w_count)
> +             free_pipe_info(pipe);
> +
> +     PIPE_UNLOCK(pipe);
> +
> +     return 0;
> +}
> +
> +static int pipe_seek(struct vnode *vnode __unused,
> +                     struct vfscore_file *file __unused,
> +                     off_t off1 __unused, off_t off2 __unused)
> +{
> +     return -EPIPE;
> +}
> +
> +static int pipe_ioctl(struct vnode *vnode, struct vfscore_file *file 
> __unused,
> +                     unsigned long com, void *data __unused)
> +{
> +     struct pipe_info *pipe = vnode->v_data;
> +     struct pipe_buf *pipe_buf __unused = pipe->buf;
> +
> +     switch (com) {
> +#ifdef FIONREAD
> +     case FIONREAD:
> +             PIPE_LOCK(pipe);
> +             *(int *)data = pipe_buf->len;
> +             PIPE_UNLOCK(pipe);
> +             return 0;
> +#endif
> +     default:
> +             return -EINVAL;
> +     }
> +}
> +
> +static struct vnops pipe_fops = {
> +     .vop_read       = pipe_read,
> +     .vop_write      = pipe_write,
> +     .vop_close      = pipe_close,
> +     .vop_seek       = pipe_seek,
> +     .vop_ioctl      = pipe_ioctl,
> +};
> +
> +struct pipe_buf *alloc_pipe_buf(int max_size)
> +{
> +     struct pipe_buf *pipe_buf;
> +
> +     pipe_buf = uk_calloc(uk_alloc_get_default(), 1, sizeof(*pipe_buf));
> +     if (!pipe_buf)
> +             return NULL;
> +     pipe_buf->data = uk_calloc(uk_alloc_get_default(), 1,
> +                                max_size * sizeof(char));
> +     if (!pipe_buf->data) {
> +             uk_free(uk_alloc_get_default(), pipe_buf);
> +             return NULL;
> +     }
> +
> +     pipe_buf->r_off = 0;
> +     pipe_buf->w_off = 0;
> +     pipe_buf->max_size = max_size;
> +     pipe_buf->len = 0;
> +
> +     return pipe_buf;
> +}
> +
> +struct pipe_info *alloc_pipe_info(int size, int flags)
> +{
> +     struct pipe_info *pipe_info;
> +
> +     pipe_info = uk_calloc(uk_alloc_get_default(), 1, sizeof(*pipe_info));
> +     if (!pipe_info)
> +             return NULL;
> +
> +     pipe_info->r_count = 1;
> +     pipe_info->w_count = 1;
> +     pipe_info->flags = flags;
> +
> +     pipe_info->buf = alloc_pipe_buf(size);
> +     if (!pipe_info->buf) {
> +             uk_free(uk_alloc_get_default(), pipe_info);
> +             return NULL;
> +     }
> +
> +     uk_mutex_init(&pipe_info->pipe_lock);
> +     uk_waitq_init(&pipe_info->wq);
> +
> +     return pipe_info;
> +}
> +
> +void free_pipe_buf(struct pipe_buf *pipe_buf)
> +{
> +     uk_free(uk_alloc_get_default(), pipe_buf->data);
> +     uk_free(uk_alloc_get_default(), pipe_buf);
> +}
> +void free_pipe_info(struct pipe_info *pipe_info)
> +{
> +     free_pipe_buf(pipe_info->buf);
> +     uk_free(uk_alloc_get_default(), pipe_info);
> +}
> +
> +
> +int pipe(int pipefd[2])
> +{
> +     int ret = 0;
> +     int w_fd, r_fd;
> +     struct dentry *w_dentry, *r_dentry;
> +     struct vnode *r_vnode, *w_vnode;
> +     struct vfscore_file *r_file, *w_file;
> +     struct pipe_info *pipe_info;
> +
> +     /* Reserve file descriptor number. */
> +     r_fd = vfscore_alloc_fd();
> +     w_fd = vfscore_alloc_fd();
> +     if (r_fd < 0 || w_fd < 0) {
> +             ret = -ENFILE;
> +             goto ERR_EXIT;
> +     }
> +
> +     /* Allocate file, dentry, and vnode. */
> +     r_file = uk_calloc(uk_alloc_get_default(), 1, sizeof(*r_file));
> +     w_file = uk_calloc(uk_alloc_get_default(), 1, sizeof(*r_file));
> +     if (!r_file || !w_file) {
> +             ret = -ENOMEM;
> +             goto ERR_MALLOC_FILE;
> +     }
> +
> +     r_dentry = uk_calloc(uk_alloc_get_default(), 1, sizeof(*r_dentry));
> +     w_dentry = uk_calloc(uk_alloc_get_default(), 1, sizeof(*r_dentry));
> +     if (!r_dentry || !w_dentry) {
> +             ret = -ENOMEM;
> +             goto ERR_MALLOC_DENTRY;
> +     }
> +
> +     r_vnode = uk_calloc(uk_alloc_get_default(), 1, sizeof(*r_vnode));
> +     w_vnode = uk_calloc(uk_alloc_get_default(), 1, sizeof(*r_vnode));
> +     if (!r_vnode || !w_vnode) {
> +             ret = -ENOMEM;
> +             goto ERR_MALLOC_VNODE;
> +     }
> +
> +     /* Allocate pipe internal structure. */
> +     pipe_info = alloc_pipe_info(PIPE_MAX_SIZE, 0);
> +     if (!pipe_info) {
> +             ret = -ENOMEM;
> +             goto ERR_ALLOC_PIPE_INFO;
> +     }
> +
> +     /* Fill out necessary fields. */
> +     r_file->fd = r_fd;
> +     w_file->fd = r_fd;
> +
> +     r_file->f_count = 1;
> +     w_file->f_count = 1;
> +
> +     r_file->f_flags = UK_FREAD;
> +     w_file->f_flags = UK_FWRITE;
> +
> +     r_file->f_dentry = r_dentry;
> +     w_file->f_dentry = w_dentry;
> +
> +     r_dentry->d_vnode = r_vnode;
> +     w_dentry->d_vnode = w_vnode;
> +
> +
> +     r_vnode->v_op = &pipe_fops;
> +     w_vnode->v_op = &pipe_fops;
> +
> +     uk_mutex_init(&r_vnode->v_lock);
> +     uk_mutex_init(&w_vnode->v_lock);
> +
> +     r_vnode->v_refcnt = 1;
> +     w_vnode->v_refcnt = 1;
> +
> +     r_vnode->v_data = pipe_info;
> +     w_vnode->v_data = pipe_info;
> +
> +     /* Assign the file descriptors to the corresponding vfs_file. */
> +     ret = vfscore_install_fd(r_fd, r_file);
> +     if (ret)
> +             goto ERR_VFS_INSTALL;
> +
> +     ret = vfscore_install_fd(w_fd, w_file);
> +     if (ret)
> +             goto ERR_VFS_INSTALL;
> +
> +     /* Fill pipefd fields. */
> +     pipefd[0] = r_fd;
> +     pipefd[1] = w_fd;

There is so much duplicated code in this function, can't we make a
static function at least for the common bits, the bits that have exactly
the same initialization values?

> +
> +     return ret;
> +
> +ERR_VFS_INSTALL:
> +     free_pipe_info(pipe_info);
> +ERR_ALLOC_PIPE_INFO:
> +     uk_free(uk_alloc_get_default(), r_vnode);
> +     uk_free(uk_alloc_get_default(), w_vnode);
> +ERR_MALLOC_VNODE:
> +     uk_free(uk_alloc_get_default(), r_dentry);
> +     uk_free(uk_alloc_get_default(), w_dentry);
> +ERR_MALLOC_DENTRY:
> +     uk_free(uk_alloc_get_default(), r_file);
> +     uk_free(uk_alloc_get_default(), w_file);
> +ERR_MALLOC_FILE:
> +     vfscore_put_fd(r_fd);
> +     vfscore_put_fd(w_fd);
> +ERR_EXIT:
> +     UK_ASSERT(ret < 0);
> +     return ret;
> +}
> +
> 

_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.