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

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



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..0fc4b1e9f100
--- /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_debug("RERROR %.*s %d\n", error.size, error.data, errcode);
+       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 46f400825af8..42677673cd34 100644
--- a/lib/uk9p/exportsyms.uk
+++ b/lib/uk9p/exportsyms.uk
@@ -2,3 +2,14 @@ uk_9pdev_trans_register
 uk_9pdev_trans_by_name
 uk_9pdev_trans_get_default
 uk_9pdev_trans_set_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__ */
-- 
2.11.0


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