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

Re: [Minios-devel] [UNIKRAFT PATCH v2 4/8] lib/uk9p: Add 9P requests abstraction



Hi Cristi,

Since we're going to have another version of 9p, I have one small
comment inline.

On 6/29/19 11:56 AM, Cristian Banu wrote:
> This patch introduces a struct uk_9preq which abstracts the lifecycle of
> allocating memory, serializing data, sending it over a transport layer,
> receiving the response, detecting errors, deserializing the received
> message and freeing the memory.
> 
> Signed-off-by: Cristian Banu <cristb@xxxxxxxxx>
> ---
>  lib/uk9p/9preq.c            | 569 
> ++++++++++++++++++++++++++++++++++++++++++++
>  lib/uk9p/Makefile.uk        |   1 +
>  lib/uk9p/exportsyms.uk      |  11 +
>  lib/uk9p/include/uk/9preq.h | 300 +++++++++++++++++++++++
>  4 files changed, 881 insertions(+)
>  create mode 100644 lib/uk9p/9preq.c
>  create mode 100644 lib/uk9p/include/uk/9preq.h
> 
> diff --git a/lib/uk9p/9preq.c b/lib/uk9p/9preq.c
> new file mode 100644
> index 000000000000..e48aae17d50f
> --- /dev/null
> +++ b/lib/uk9p/9preq.c
> @@ -0,0 +1,569 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +/*
> + * Authors: Cristian Banu <cristb@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 <string.h>
> +#include <uk/config.h>
> +#include <uk/9preq.h>
> +#include <uk/9p_core.h>
> +#include <uk/list.h>
> +#include <uk/refcount.h>
> +#include <uk/essentials.h>
> +#include <uk/alloc.h>
> +#if CONFIG_LIBUKSCHED
> +#include <uk/sched.h>
> +#include <uk/wait.h>
> +#endif
> +
> +static int _fcall_alloc(struct uk_alloc *a, struct uk_9preq_fcall *f,
> +                     uint32_t size)
> +{
> +     UK_ASSERT(a);
> +     UK_ASSERT(f);
> +     UK_ASSERT(size > 0);
> +
> +     f->buf = uk_calloc(a, size, sizeof(char));
> +     if (f->buf == NULL)
> +             return -ENOMEM;
> +
> +     f->size = size;
> +     f->offset = 0;
> +     f->zc_buf = NULL;
> +     f->zc_size = 0;
> +     f->zc_offset = 0;
> +
> +     return 0;
> +}
> +
> +static void _fcall_free(struct uk_alloc *a, struct uk_9preq_fcall *f)
> +{
> +     UK_ASSERT(a);
> +     UK_ASSERT(f);
> +
> +     if (f->buf)
> +             uk_free(a, f->buf);
> +}
> +
> +struct uk_9preq *uk_9preq_alloc(struct uk_alloc *a, uint32_t size)
> +{
> +     struct uk_9preq *req;
> +     int rc;
> +
> +     req = uk_calloc(a, 1, sizeof(*req));
> +     if (req == NULL)
> +             goto out;
> +
> +     rc = _fcall_alloc(a, &req->xmit, size);
> +     if (rc < 0)
> +             goto out_free;
> +
> +     rc = _fcall_alloc(a, &req->recv, MAX(size, UK_9P_RERROR_MAXSIZE));
> +     if (rc < 0)
> +             goto out_free;
> +
> +     UK_INIT_LIST_HEAD(&req->_list);
> +     req->_a = a;
> +     uk_refcount_init(&req->refcount, 1);
> +#if CONFIG_LIBUKSCHED
> +     uk_waitq_init(&req->wq);
> +#endif
> +
> +     /*
> +      * Assume the header has already been written.
> +      * The header itself will be written on uk_9preq_ready(), when the
> +      * actual message size is known.
> +      */
> +     req->xmit.offset = UK_9P_HEADER_SIZE;
> +
> +     return req;
> +
> +out_free:
> +     _fcall_free(a, &req->recv);
> +     _fcall_free(a, &req->xmit);
> +     uk_free(a, req);
> +out:
> +     return NULL;
> +}
> +
> +static void _req_free(struct uk_9preq *req)
> +{
> +     _fcall_free(req->_a, &req->recv);
> +     _fcall_free(req->_a, &req->xmit);
> +     uk_free(req->_a, req);
> +}
> +
> +void uk_9preq_get(struct uk_9preq *req)
> +{
> +     uk_refcount_acquire(&req->refcount);
> +}
> +
> +int uk_9preq_put(struct uk_9preq *req)
> +{
> +     int last;
> +
> +     last = uk_refcount_release(&req->refcount);
> +     if (last)
> +             _req_free(req);
> +
> +     return last;
> +}
> +
> +static int _fcall_write(struct uk_9preq_fcall *fcall, const void *buf,
> +             uint32_t size)
> +{
> +     if (fcall->offset + size > fcall->size)
> +             return -ENOBUFS;
> +
> +     memcpy((char *)fcall->buf + fcall->offset, buf, size);
> +     fcall->offset += size;
> +     return 0;
> +}
> +
> +static int _fcall_serialize(struct uk_9preq_fcall *f, const char *fmt, ...);
> +
> +static int _fcall_vserialize(struct uk_9preq_fcall *fcall, const char *fmt,
> +                     va_list vl)
> +{
> +     int rc = 0;
> +
> +     while (*fmt) {
> +             switch (*fmt) {
> +             case 'b': {
> +                     uint8_t x;
> +
> +                     x = va_arg(vl, unsigned int);
> +                     rc = _fcall_write(fcall, &x, sizeof(x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'w': {
> +                     uint16_t x;
> +
> +                     x = va_arg(vl, unsigned int);
> +                     rc = _fcall_write(fcall, &x, sizeof(x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'd': {
> +                     uint32_t x;
> +
> +                     x = va_arg(vl, uint32_t);
> +                     rc = _fcall_write(fcall, &x, sizeof(x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'q': {
> +                     uint64_t x;
> +
> +                     x = va_arg(vl, uint64_t);
> +                     rc = _fcall_write(fcall, &x, sizeof(x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 's': {
> +                     struct uk_9p_str *p;
> +
> +                     p = va_arg(vl, struct uk_9p_str *);
> +                     rc = _fcall_write(fcall, &p->size, sizeof(p->size));
> +                     if (rc < 0)
> +                             goto out;
> +                     rc = _fcall_write(fcall, p->data, p->size);
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'Q': {
> +                     struct uk_9p_qid *p;
> +
> +                     p = va_arg(vl, struct uk_9p_qid *);
> +                     rc = _fcall_serialize(fcall, "bdq", p->type,
> +                                     p->version, p->path);
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'S': {
> +                     struct uk_9p_stat *p;
> +
> +                     p = va_arg(vl, struct uk_9p_stat *);
> +                     rc = _fcall_serialize(fcall, "wwdQdddqsssssddd",
> +                                     p->size, p->type, p->dev, &p->qid,
> +                                     p->mode, p->atime, p->mtime, p->length,
> +                                     &p->name, &p->uid, &p->gid, &p->muid,
> +                                     &p->extension, p->n_uid, p->n_gid,
> +                                     p->n_muid);
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             default:
> +                     rc = -EINVAL;
> +                     goto out;
> +             }
> +
> +             fmt++;
> +     }
> +
> +out:
> +     return rc;
> +}
> +
> +static int _fcall_serialize(struct uk_9preq_fcall *f, const char *fmt, ...)
> +{
> +     va_list vl;
> +     int rc;
> +
> +     va_start(vl, fmt);
> +     rc = _fcall_vserialize(f, fmt, vl);
> +     va_end(vl);
> +
> +     return rc;
> +}
> +
> +int uk_9preq_vserialize(struct uk_9preq *req, const char *fmt, va_list vl)
> +{
> +     int rc;
> +
> +     UK_ASSERT(req);
> +     UK_ASSERT(UK_READ_ONCE(req->state) == UK_9PREQ_INITIALIZED);
> +     rc = _fcall_vserialize(&req->xmit, fmt, vl);
> +
> +     return rc;
> +}
> +
> +int uk_9preq_serialize(struct uk_9preq *req, const char *fmt, ...)
> +{
> +     va_list vl;
> +     int rc;
> +
> +     va_start(vl, fmt);
> +     rc = uk_9preq_vserialize(req, fmt, vl);
> +     va_end(vl);
> +
> +     return rc;
> +}
> +
> +static int _fcall_read(struct uk_9preq_fcall *fcall, void *buf, uint32_t 
> size)
> +{
> +     if (fcall->offset + size > fcall->size)
> +             return -ENOBUFS;
> +
> +     memcpy(buf, (char *)fcall->buf + fcall->offset, size);
> +     fcall->offset += size;
> +     return 0;
> +}
> +
> +static int _fcall_deserialize(struct uk_9preq_fcall *f, const char *fmt, 
> ...);
> +
> +static int _fcall_vdeserialize(struct uk_9preq_fcall *fcall,
> +                           const char *fmt,
> +                           va_list vl)
> +{
> +     int rc = 0;
> +
> +     while (*fmt) {
> +             switch (*fmt) {
> +             case 'b': {
> +                     uint8_t *x;
> +
> +                     x = va_arg(vl, uint8_t *);
> +                     rc = _fcall_read(fcall, x, sizeof(*x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'w': {
> +                     uint16_t *x;
> +
> +                     x = va_arg(vl, uint16_t *);
> +                     rc = _fcall_read(fcall, x, sizeof(*x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'd': {
> +                     uint32_t *x;
> +
> +                     x = va_arg(vl, uint32_t *);
> +                     rc = _fcall_read(fcall, x, sizeof(*x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'q': {
> +                     uint64_t *x;
> +
> +                     x = va_arg(vl, uint64_t *);
> +                     rc = _fcall_read(fcall, x, sizeof(*x));
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 's': {
> +                     struct uk_9p_str *p;
> +
> +                     p = va_arg(vl, struct uk_9p_str *);
> +                     rc = _fcall_read(fcall, &p->size, sizeof(p->size));
> +                     if (rc < 0)
> +                             goto out;
> +                     p->data = (char *)fcall->buf + fcall->offset;
> +                     fcall->offset += p->size;
> +                     break;
> +             }
> +             case 'Q': {
> +                     struct uk_9p_qid *p;
> +
> +                     p = va_arg(vl, struct uk_9p_qid *);
> +                     rc = _fcall_deserialize(fcall, "bdq", &p->type,
> +                                     &p->version, &p->path);
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             case 'S': {
> +                     struct uk_9p_stat *p;
> +
> +                     p = va_arg(vl, struct uk_9p_stat *);
> +                     rc = _fcall_deserialize(fcall, "wwdQdddqsssssddd",
> +                                     &p->size, &p->type, &p->dev, &p->qid,
> +                                     &p->mode, &p->atime, &p->mtime,
> +                                     &p->length, &p->name, &p->uid, &p->gid,
> +                                     &p->muid, &p->extension, &p->n_uid,
> +                                     &p->n_gid, &p->n_muid);
> +                     if (rc < 0)
> +                             goto out;
> +                     break;
> +             }
> +             default:
> +                     rc = -EINVAL;
> +                     goto out;
> +             }
> +
> +             fmt++;
> +     }
> +
> +out:
> +     return rc;
> +}
> +
> +static int _fcall_deserialize(struct uk_9preq_fcall *f, const char *fmt, ...)
> +{
> +     va_list vl;
> +     int rc;
> +
> +     va_start(vl, fmt);
> +     rc = _fcall_vdeserialize(f, fmt, vl);
> +     va_end(vl);
> +
> +     return rc;
> +}
> +
> +int uk_9preq_vdeserialize(struct uk_9preq *req, const char *fmt, va_list vl)
> +{
> +     int rc;
> +
> +     UK_ASSERT(req);
> +     UK_ASSERT(UK_READ_ONCE(req->state) == UK_9PREQ_RECEIVED);
> +     rc = _fcall_vdeserialize(&req->recv, fmt, vl);
> +
> +     return rc;
> +}
> +
> +int uk_9preq_deserialize(struct uk_9preq *req, const char *fmt, ...)
> +{
> +     va_list vl;
> +     int rc;
> +
> +     va_start(vl, fmt);
> +     rc = uk_9preq_vdeserialize(req, fmt, vl);
> +     va_end(vl);
> +
> +     return rc;
> +}
> +
> +int uk_9preq_copy_to(struct uk_9preq *req, void *buf, uint32_t size)
> +{
> +     return _fcall_read(&req->recv, buf, size);
> +}
> +
> +int uk_9preq_copy_from(struct uk_9preq *req, const void *buf, uint32_t size)
> +{
> +     return _fcall_write(&req->xmit, buf, size);
> +}
> +
> +int uk_9preq_ready(struct uk_9preq *req, enum uk_9preq_zcdir zc_dir,
> +             void *zc_buf, uint32_t zc_size, uint32_t zc_offset)
> +{
> +     int rc;
> +     uint32_t total_size;
> +     uint32_t total_size_with_zc;
> +
> +     UK_ASSERT(req);
> +
> +     if (UK_READ_ONCE(req->state) != UK_9PREQ_INITIALIZED) {
> +             rc = -EIO;
> +             goto out;
> +     }
> +
> +     /* Save current offset as the size of the message. */
> +     total_size = req->xmit.offset;
> +
> +     total_size_with_zc = total_size;
> +     if (zc_dir == UK_9PREQ_ZCDIR_WRITE)
> +             total_size_with_zc += zc_size;
> +
> +     /* Serialize the header. */
> +     req->xmit.offset = 0;
> +     rc = uk_9preq_serialize(req, "dbw", total_size_with_zc, req->xmit.type,
> +                     req->tag);
> +     if (rc < 0)
> +             goto out;
> +
> +     /* Reset offset and size to sane values. */
> +     req->xmit.offset = 0;
> +     req->xmit.size = total_size;
> +
> +     /* Update zero copy buffers. */
> +     if (zc_dir == UK_9PREQ_ZCDIR_WRITE) {
> +             req->xmit.zc_buf = zc_buf;
> +             req->xmit.zc_size = zc_size;
> +             /* Zero-copy offset for writes must start at the end of buf. */
> +             req->xmit.zc_offset = req->xmit.size;
> +     } else if (zc_dir == UK_9PREQ_ZCDIR_READ) {
> +             req->recv.zc_buf = zc_buf;
> +             req->recv.zc_size = zc_size;
> +             req->recv.zc_offset = zc_offset;
> +             /* The receive buffer must end before the zc buf. */
> +             req->recv.size = zc_offset;
> +     }
> +
> +     /* Update the state. */
> +     UK_WRITE_ONCE(req->state, UK_9PREQ_READY);
> +
> +out:
> +     return rc;
> +}
> +
> +int uk_9preq_receive_cb(struct uk_9preq *req, uint32_t recv_size)
> +{
> +     uint32_t size;
> +     uint16_t tag;
> +     int rc;
> +
> +     UK_ASSERT(req);
> +
> +     /* Check state and the existence of the header. */
> +     if (UK_READ_ONCE(req->state) != UK_9PREQ_SENT)
> +             return -EIO;
> +     if (recv_size < UK_9P_HEADER_SIZE)
> +             return -EIO;
> +
> +     /* Deserialize the header into request fields. */
> +     req->recv.offset = 0;
> +     req->recv.size = recv_size;
> +     rc = _fcall_deserialize(&req->recv, "dbw", &size,
> +                     &req->recv.type, &tag);
> +
> +     /* Check sanity of deserialized values. */
> +     if (rc < 0)
> +             return rc;
> +     if (size > recv_size)
> +             return -EIO;
> +     if (req->tag != tag)
> +             return -EIO;
> +
> +     /* Fix the receive size for zero-copy requests. */
> +     if (req->recv.zc_buf && req->recv.type != UK_9P_RERROR)
> +             req->recv.size = req->recv.zc_offset;
> +     else
> +             req->recv.size = size;
> +
> +     /* Update the state. */
> +     UK_WRITE_ONCE(req->state, UK_9PREQ_RECEIVED);
> +
> +#if CONFIG_LIBUKSCHED
> +     /* Notify any waiting threads. */
> +     uk_waitq_wake_up(&req->wq);
> +#endif
> +
> +     return 0;
> +}
> +
> +int uk_9preq_waitreply(struct uk_9preq *req)
> +{
> +     int rc;
> +
> +#if CONFIG_LIBUKSCHED
> +     uk_waitq_wait_event(&req->wq, req->state == UK_9PREQ_RECEIVED);
> +#else
> +     while (UK_READ_ONCE(req->state) != UK_9PREQ_RECEIVED)
> +             ;
> +#endif
> +
> +     /* Check for 9P server-side errors. */
> +     rc = uk_9preq_error(req);
> +
> +     return rc;
> +}
> +
> +int uk_9preq_error(struct uk_9preq *req)
> +{
> +     uint32_t errcode;
> +     struct uk_9p_str error;
> +     int rc = 0;
> +
> +     if (UK_READ_ONCE(req->state) != UK_9PREQ_RECEIVED)
> +             return -EIO;
> +     if (req->recv.type != UK_9P_RERROR)
> +             return 0;
> +
> +     /*
> +      * The request should not have had any data deserialized from it prior
> +      * to this call.
> +      */
> +     UK_BUGON(req->recv.offset != UK_9P_HEADER_SIZE);
> +
> +     rc = uk_9preq_deserialize(req, "sd", &error, &errcode);
> +     if (rc < 0)
> +             return rc;
> +
> +     uk_pr_info("uk9p: RERROR %.*s %d\n", error.size, error.data, errcode);

I think we should use uk_pr_debug() instead. The errors should be
printed on a higher level, if needed, here we need them only as debug
messages. Otherwise we will have lots of message printed when testing if
files/directories exist.

> +     if (errcode == 0 || errcode >= 512)
> +             return -EIO;
> +
> +     return -errcode;
> +}
> diff --git a/lib/uk9p/Makefile.uk b/lib/uk9p/Makefile.uk
> index b1071a0e7d3c..aea722a585b9 100644
> --- a/lib/uk9p/Makefile.uk
> +++ b/lib/uk9p/Makefile.uk
> @@ -4,3 +4,4 @@ CINCLUDES-$(CONFIG_LIBUK9P)           += 
> -I$(LIBUK9P_BASE)/include
>  CXXINCLUDES-$(CONFIG_LIBUK9P)                += -I$(LIBUK9P_BASE)/include
>  
>  LIBUK9P_SRCS-y += $(LIBUK9P_BASE)/9pdev_trans.c
> +LIBUK9P_SRCS-y += $(LIBUK9P_BASE)/9preq.c
> diff --git a/lib/uk9p/exportsyms.uk b/lib/uk9p/exportsyms.uk
> index 45f487da2fff..850ffa2db233 100644
> --- a/lib/uk9p/exportsyms.uk
> +++ b/lib/uk9p/exportsyms.uk
> @@ -1,3 +1,14 @@
>  uk_9pdev_trans_register
>  uk_9pdev_trans_by_name
>  uk_9pdev_trans_default
> +uk_9preq_get
> +uk_9preq_put
> +uk_9preq_vserialize
> +uk_9preq_serialize
> +uk_9preq_vdeserialize
> +uk_9preq_deserialize
> +uk_9preq_copy_to
> +uk_9preq_copy_from
> +uk_9preq_receive_cb
> +uk_9preq_waitreply
> +uk_9preq_error
> diff --git a/lib/uk9p/include/uk/9preq.h b/lib/uk9p/include/uk/9preq.h
> new file mode 100644
> index 000000000000..ee4d2af8635d
> --- /dev/null
> +++ b/lib/uk9p/include/uk/9preq.h
> @@ -0,0 +1,300 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +/*
> + * Authors: Cristian Banu <cristb@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.
> + */
> +
> +#ifndef __UK_9PREQ__
> +#define __UK_9PREQ__
> +
> +#include <inttypes.h>
> +#include <uk/config.h>
> +#include <uk/alloc.h>
> +#include <uk/essentials.h>
> +#include <uk/list.h>
> +#include <uk/refcount.h>
> +#if CONFIG_LIBUKSCHED
> +#include <uk/wait_types.h>
> +#endif
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/*
> + * The header consists of the following fields: size (4 bytes), type (1) and
> + * tag (2).
> + */
> +#define UK_9P_HEADER_SIZE               7U
> +
> +/*
> + * The maximum buffer size for an error reply is given by the header (7), the
> + * string size (2), the error string (128) and the error code (4): in total,
> + * 141.
> + */
> +#define UK_9P_RERROR_MAXSIZE            141U
> +
> +/**
> + * @internal
> + *
> + * Describes the 9p zero-copy direction.
> + */
> +enum uk_9preq_zcdir {
> +     UK_9PREQ_ZCDIR_NONE,
> +     UK_9PREQ_ZCDIR_READ,
> +     UK_9PREQ_ZCDIR_WRITE,
> +};
> +
> +/**
> + * @internal
> + *
> + * Describes a 9p fcall structure.
> + */
> +struct uk_9preq_fcall {
> +     /*
> +      * Total size of the fcall. Initially, this is the buffer size.
> +      * After ready (on xmit) or reply (on recv), this will be the size of
> +      * the sent/received data.
> +      */
> +     uint32_t                        size;
> +     /* Type of the fcall. Should be T* for transmit, R* for receive. */
> +     uint8_t                         type;
> +     /* Offset while serializing or deserializing. */
> +     uint32_t                        offset;
> +     /* Buffer pointer. */
> +     void                            *buf;
> +
> +     /* Zero-copy buffer pointer. */
> +     void                            *zc_buf;
> +     /* Zero-copy buffer size. */
> +     uint32_t                        zc_size;
> +     /* Zero-copy buffer offset in the 9P message. */
> +     uint32_t                        zc_offset;
> +};
> +
> +/**
> + * Describes the possible states in which a request may be.
> + *
> + * - NONE: Right after allocating.
> + * - INITIALIZED: Request is ready to receive serialization data.
> + * - READY: Request is ready to be sent.
> + * - RECEIVED: Transport layer has received the reply and important data such
> + *   as the tag, type and size have been validated.
> + */
> +enum uk_9preq_state {
> +     UK_9PREQ_NONE = 0,
> +     UK_9PREQ_INITIALIZED,
> +     UK_9PREQ_READY,
> +     UK_9PREQ_SENT,
> +     UK_9PREQ_RECEIVED
> +};
> +
> +/**
> + *  Describes a 9P request.
> + *
> + *  This gets allocated via uk_9pdev_req_create(), and freed when it is not
> + *  referenced anymore. A call to uk_9pdev_req_remove() is mandatory to
> + *  correctly free this and remove it from the list of requests managed
> + *  by the 9p device.
> + */
> +struct uk_9preq {
> +     /* Transmit fcall. */
> +     struct uk_9preq_fcall           xmit;
> +     /* Receive fcall. */
> +     struct uk_9preq_fcall           recv;
> +     /* State of the request. See the state enum for details. */
> +     enum uk_9preq_state             state;
> +     /* Tag allocated to this request. */
> +     uint16_t                        tag;
> +     /* Entry into the list of requests (API-internal). */
> +     struct uk_list_head             _list;
> +     /* @internal Allocator used to allocate this request. */
> +     struct uk_alloc                 *_a;
> +     /* Tracks the number of references to this structure. */
> +     __atomic                        refcount;
> +#if CONFIG_LIBUKSCHED
> +     /* Wait-queue for state changes. */
> +     struct uk_waitq                 wq;
> +#endif
> +};
> +
> +/**
> + * @internal
> + * Allocates a 9p request.
> + * Should not be used directly, use uk_9pdev_req_create() instead.
> + *
> + * @param a
> + *   Allocator to use.
> + * @param size
> + *   Minimum size of the receive and transmit buffers.
> + * @return
> + *   - (==NULL): Out of memory.
> + *   - (!=NULL): Successful.
> + */
> +struct uk_9preq *uk_9preq_alloc(struct uk_alloc *a, uint32_t size);
> +
> +/**
> + * Gets the 9p request, incrementing the reference count.
> + *
> + * @param req
> + *   Reference to the 9p request.
> + */
> +void uk_9preq_get(struct uk_9preq *req);
> +
> +/**
> + * Puts the 9p request, decrementing the reference count.
> + * If this was the last live reference, the memory will be freed.
> + *
> + * @param req
> + *   Reference to the 9p request.
> + * @return
> + *   - 0: This was not the last live reference.
> + *   - 1: This was the last live reference.
> + */
> +int uk_9preq_put(struct uk_9preq *req);
> +
> +/*
> + * The following family of serialization and deserialization functions work
> + * by employing a printf-like formatting mechanism for data types supported 
> by
> + * the 9p protocol:
> + * - 'b': byte (uint8_t)
> + * - 'w': word (uint16_t)
> + * - 'd': double-word (uint32_t)
> + * - 'q': quad-word (uint64_t)
> + * - 's': uk_9p_str *
> + * - 'S': uk_9p_stat *
> + *
> + * Similarly to vprintf(), the vserialize() and vdeserialize() functions take
> + * a va_list instead of a variable number of arguments.
> + *
> + * Possible return values:
> + * - 0: Operation successful.
> + * - (-EINVAL): Invalid format specifier.
> + * - (-ENOBUFS): End of buffer reached.
> + */
> +
> +int uk_9preq_vserialize(struct uk_9preq *req, const char *fmt, va_list vl);
> +int uk_9preq_serialize(struct uk_9preq *req, const char *fmt, ...);
> +int uk_9preq_vdeserialize(struct uk_9preq *req, const char *fmt, va_list vl);
> +int uk_9preq_deserialize(struct uk_9preq *req, const char *fmt, ...);
> +
> +/**
> + * Copies raw data from the request receive buffer to the provided buffer.
> + *
> + * @param req
> + *   Reference to the 9p request.
> + * @param buf
> + *   Destination buffer.
> + * @param size
> + *   Amount to copy.
> + * Possible return values:
> + * - 0: Operation successful.
> + * - (-ENOBUFS): End of buffer reached.
> + */
> +int uk_9preq_copy_to(struct uk_9preq *req, void *buf, uint32_t size);
> +
> +/**
> + * Copies raw data from the provided buffer to the request transmission 
> buffer.
> + *
> + * @param req
> + *   Reference to the 9p request.
> + * @param buf
> + *   Source buffer.
> + * @param size
> + *   Amount to copy.
> + * Possible return values:
> + * - 0: Operation successful.
> + * - (-ENOBUFS): End of buffer reached.
> + */
> +int uk_9preq_copy_from(struct uk_9preq *req, const void *buf, uint32_t size);
> +
> +/**
> + * Marks the given request as being ready, transitioning between states
> + * INITIALIZED and READY.
> + *
> + * @param req
> + *   Reference to the 9p request.
> + * @param zc_dir
> + *   Zero-copy direction.
> + * @param zc_buf
> + *   Zero-copy buffer, if zc_dir is not NONE.
> + * @param zc_size
> + *   Zero-copy buffer size, if zc_dir is not NONE.
> + * @param zc_offset
> + *   Zero-copy offset within the received message, if zc_dir is READ.
> + * @return
> + *   - 0: Successful.
> + *   - (< 0): Invalid state or request size serialization failed.
> + */
> +int uk_9preq_ready(struct uk_9preq *req, enum uk_9preq_zcdir zc_dir,
> +             void *zc_buf, uint32_t zc_size, uint32_t zc_offset);
> +
> +/**
> + * Function called from the transport layer when a request has been received.
> + * Implements the transition from the SENT to the RECEIVED state.
> + *
> + * @param req
> + *   The 9P request.
> + * @param recv_size
> + *   Size of the packet received from the transport layer.
> + * @return
> + *   - (0): Successfully received.
> + *   - (< 0): An error occurred.
> + */
> +int uk_9preq_receive_cb(struct uk_9preq *req, uint32_t recv_size);
> +
> +/**
> + * Waits for the reply to be received.
> + *
> + * @param req
> + *   The 9P request.
> + * @return
> + *   - (0): Successful.
> + *   - (< 0): Failed. Returns the error code received from the 9P server.
> + */
> +int uk_9preq_waitreply(struct uk_9preq *req);
> +
> +/**
> + * Extracts the error from the received reply.
> + *
> + * @param req
> + *   The 9P request.
> + * @return
> + *   - (0): No error occurred.
> + *   - (< 0): An Rerror was received, the error code is 9pfs-specific.
> + */
> +int uk_9preq_error(struct uk_9preq *req);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* __UK_9PREQ__ */
> 

_______________________________________________
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®.