|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [UNIKRAFT PATCH 4/7] lib/uk9p: Recycle uk_9preq allocations
Each 9p device keeps a free-list of 9p requests. This allows
allocations to take much less time, as usually the workflow of a 9p
device is synchronous: create request, wait for reply, process it,
repeat. This approach avoids the allocation performance hit on
subsequent request creations.
Results below indicate that request_allocated takes < 100ns,
as opposed to 1us (previous commit in this patch series).
In the sample below, the read() now spends most of the time (~99.7%)
either notifying the host or waiting for a reply. At this scale,
even uk_traces are probably noisy and the "real" time might be lower
by a relatively large amount.
Excerpt from the uk traces on a read() call with in-depth tracing:
1846467237 uk_9p_trace_read fid 2 offset 61440 count 4096
1846467321 uk_9p_trace_request_allocated
1846467350 uk_9p_trace_ready tag 0
1846470161 uk_9p_trace_sent tag 0
1846508851 uk_9p_trace_received tag 0
1846508889 uk_9p_trace_read_end count 4096
Signed-off-by: Cristian Banu <cristb@xxxxxxxxx>
---
lib/uk9p/9pdev.c | 78 +++++++++++++++++++++++++++++---
lib/uk9p/9preq.c | 14 ++----
lib/uk9p/include/uk/9pdev.h | 10 ++++
lib/uk9p/include/uk/9pdev_core.h | 2 +
lib/uk9p/include/uk/9preq.h | 15 +++---
5 files changed, 93 insertions(+), 26 deletions(-)
diff --git a/lib/uk9p/9pdev.c b/lib/uk9p/9pdev.c
index 40a4daa..42c5058 100644
--- a/lib/uk9p/9pdev.c
+++ b/lib/uk9p/9pdev.c
@@ -132,6 +132,7 @@ static void _req_mgmt_init(struct uk_9pdev_req_mgmt
*req_mgmt)
ukarch_spin_lock_init(&req_mgmt->spinlock);
uk_bitmap_zero(req_mgmt->tag_bm, UK_9P_NUMTAGS);
UK_INIT_LIST_HEAD(&req_mgmt->req_list);
+ UK_INIT_LIST_HEAD(&req_mgmt->req_free_list);
}
static void _req_mgmt_add_req_locked(struct uk_9pdev_req_mgmt *req_mgmt,
@@ -141,6 +142,21 @@ static void _req_mgmt_add_req_locked(struct
uk_9pdev_req_mgmt *req_mgmt,
uk_list_add(&req->_list, &req_mgmt->req_list);
}
+static struct uk_9preq *
+_req_mgmt_from_freelist_locked(struct uk_9pdev_req_mgmt *req_mgmt)
+{
+ struct uk_9preq *req;
+
+ if (uk_list_empty(&req_mgmt->req_free_list))
+ return NULL;
+
+ req = uk_list_first_entry(&req_mgmt->req_free_list,
+ struct uk_9preq, _list);
+ uk_list_del(&req->_list);
+
+ return req;
+}
+
static void _req_mgmt_del_req_locked(struct uk_9pdev_req_mgmt *req_mgmt,
struct uk_9preq *req)
{
@@ -148,6 +164,12 @@ static void _req_mgmt_del_req_locked(struct
uk_9pdev_req_mgmt *req_mgmt,
uk_list_del(&req->_list);
}
+static void _req_mgmt_req_to_freelist_locked(struct uk_9pdev_req_mgmt
*req_mgmt,
+ struct uk_9preq *req)
+{
+ uk_list_add(&req->_list, &req_mgmt->req_free_list);
+}
+
static uint16_t _req_mgmt_next_tag_locked(struct uk_9pdev_req_mgmt *req_mgmt)
{
return uk_find_next_zero_bit(req_mgmt->tag_bm, UK_9P_NUMTAGS, 0);
@@ -164,10 +186,24 @@ static void _req_mgmt_cleanup(struct uk_9pdev_req_mgmt
*req_mgmt __unused)
tag = req->tag;
_req_mgmt_del_req_locked(req_mgmt, req);
if (!uk_9preq_put(req)) {
- uk_pr_warn("Tag %d still has references on cleanup.\n",
+ /* If in the future these references get released, mark
+ * _dev as NULL so uk_9pdev_req_to_freelist doesn't
+ * attempt to place them in an invalid memory region.
+ *
+ * As _dev is not used for any other purpose, this
+ * doesn't impact any other logic related to 9p request
+ * processing.
+ */
+ req->_dev = NULL;
+ uk_pr_err("Tag %d still has references on cleanup.\n",
tag);
}
}
+ uk_list_for_each_entry_safe(req, reqn, &req_mgmt->req_free_list,
+ _list) {
+ uk_list_del(&req->_list);
+ uk_free(req->_a, req);
+ }
ukplat_spin_unlock_irqrestore(&req_mgmt->spinlock, flags);
}
@@ -297,17 +333,35 @@ struct uk_9preq *uk_9pdev_req_create(struct uk_9pdev
*dev, uint8_t type)
UK_ASSERT(dev);
- req = uk_9preq_alloc(dev->a);
- if (req == NULL) {
- rc = -ENOMEM;
- goto out;
+ ukplat_spin_lock_irqsave(&dev->_req_mgmt.spinlock, flags);
+ if (!(req = _req_mgmt_from_freelist_locked(&dev->_req_mgmt))) {
+ /* Don't allocate with the spinlock held. */
+ ukplat_spin_unlock_irqrestore(&dev->_req_mgmt.spinlock, flags);
+ req = uk_calloc(dev->a, 1, sizeof(*req));
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ req->_dev = dev;
+ /*
+ * Duplicate this, instead of using req->_dev, as we can't rely
+ * on the value of _dev at time of free. Check comment in
+ * _req_mgmt_cleanup.
+ */
+ req->_a = dev->a;
+ ukplat_spin_lock_irqsave(&dev->_req_mgmt.spinlock, flags);
}
+ uk_9preq_init(req);
+
+ /* If request was from the free list, it should already belong to the
+ * dev. */
+ UK_ASSERT(req->_dev == dev);
+
/* Shouldn't exceed the msize on non-zerocopy buffers, just in case. */
req->recv.size = MIN(req->recv.size, dev->msize);
req->xmit.size = MIN(req->xmit.size, dev->msize);
- ukplat_spin_lock_irqsave(&dev->_req_mgmt.spinlock, flags);
if (type == UK_9P_TVERSION)
tag = UK_9P_NOTAG;
else
@@ -360,6 +414,18 @@ int uk_9pdev_req_remove(struct uk_9pdev *dev, struct
uk_9preq *req)
return uk_9preq_put(req);
}
+void uk_9pdev_req_to_freelist(struct uk_9pdev *dev, struct uk_9preq *req)
+{
+ unsigned long flags;
+
+ if (!dev)
+ return;
+
+ ukplat_spin_lock_irqsave(&dev->_req_mgmt.spinlock, flags);
+ _req_mgmt_req_to_freelist_locked(&dev->_req_mgmt, req);
+ ukplat_spin_unlock_irqrestore(&dev->_req_mgmt.spinlock, flags);
+}
+
struct uk_9pfid *uk_9pdev_fid_create(struct uk_9pdev *dev)
{
struct uk_9pfid *fid = NULL;
diff --git a/lib/uk9p/9preq.c b/lib/uk9p/9preq.c
index d44e684..edc462c 100644
--- a/lib/uk9p/9preq.c
+++ b/lib/uk9p/9preq.c
@@ -35,6 +35,7 @@
#include <string.h>
#include <uk/config.h>
#include <uk/9preq.h>
+#include <uk/9pdev.h>
#include <uk/9p_core.h>
#include <uk/list.h>
#include <uk/refcount.h>
@@ -45,14 +46,8 @@
#include <uk/wait.h>
#endif
-struct uk_9preq *uk_9preq_alloc(struct uk_alloc *a)
+void uk_9preq_init(struct uk_9preq *req)
{
- struct uk_9preq *req;
-
- req = uk_calloc(a, 1, sizeof(*req));
- if (req == NULL)
- return NULL;
-
req->xmit.buf = req->xmit_buf;
req->recv.buf = req->recv_buf;
req->xmit.size = req->recv.size = UK_9P_BUFSIZE;
@@ -69,13 +64,10 @@ struct uk_9preq *uk_9preq_alloc(struct uk_alloc *a)
req->recv.offset = 0;
UK_INIT_LIST_HEAD(&req->_list);
- req->_a = a;
uk_refcount_init(&req->refcount, 1);
#if CONFIG_LIBUKSCHED
uk_waitq_init(&req->wq);
#endif
-
- return req;
}
void uk_9preq_get(struct uk_9preq *req)
@@ -89,7 +81,7 @@ int uk_9preq_put(struct uk_9preq *req)
last = uk_refcount_release(&req->refcount);
if (last)
- uk_free(req->_a, req);
+ uk_9pdev_req_to_freelist(req->_dev, req);
return last;
}
diff --git a/lib/uk9p/include/uk/9pdev.h b/lib/uk9p/include/uk/9pdev.h
index 1225336..560ba8f 100644
--- a/lib/uk9p/include/uk/9pdev.h
+++ b/lib/uk9p/include/uk/9pdev.h
@@ -149,6 +149,16 @@ struct uk_9preq *uk_9pdev_req_lookup(struct uk_9pdev *dev,
uint16_t tag);
*/
int uk_9pdev_req_remove(struct uk_9pdev *dev, struct uk_9preq *req);
+/**
+ * Places the given request on the 9p device's request freelist.
+ *
+ * @param dev
+ * The Unikraft 9P Device. If NULL, doesn't place the request on the
freelist.
+ * @param req
+ * The request to be placed on the freelist.
+ */
+void uk_9pdev_req_to_freelist(struct uk_9pdev *dev, struct uk_9preq *req);
+
/**
* Creates a FID associated with the given 9P device.
*
diff --git a/lib/uk9p/include/uk/9pdev_core.h b/lib/uk9p/include/uk/9pdev_core.h
index 38864ac..fcad1ef 100644
--- a/lib/uk9p/include/uk/9pdev_core.h
+++ b/lib/uk9p/include/uk/9pdev_core.h
@@ -120,6 +120,8 @@ struct uk_9pdev_req_mgmt {
unsigned long tag_bm[UK_BITS_TO_LONGS(UK_9P_NUMTAGS)];
/* List of requests allocated and not yet removed. */
struct uk_list_head req_list;
+ /* Free-list of requests. */
+ struct uk_list_head req_free_list;
};
/**
diff --git a/lib/uk9p/include/uk/9preq.h b/lib/uk9p/include/uk/9preq.h
index ed883d2..aad8d42 100644
--- a/lib/uk9p/include/uk/9preq.h
+++ b/lib/uk9p/include/uk/9preq.h
@@ -162,6 +162,8 @@ struct uk_9preq {
uint16_t tag;
/* Entry into the list of requests (API-internal). */
struct uk_list_head _list;
+ /* @internal 9P device this request belongs to. */
+ struct uk_9pdev *_dev;
/* @internal Allocator used to allocate this request. */
struct uk_alloc *_a;
/* Tracks the number of references to this structure. */
@@ -176,16 +178,10 @@ UK_CTASSERT(sizeof(struct uk_9preq) <= __PAGE_SIZE);
/**
* @internal
- * Allocates a 9p request.
+ * Initializes a 9P request.
* Should not be used directly, use uk_9pdev_req_create() instead.
- *
- * @param a
- * Allocator to use.
- * @return
- * - (==NULL): Out of memory.
- * - (!=NULL): Successful.
*/
-struct uk_9preq *uk_9preq_alloc(struct uk_alloc *a);
+void uk_9preq_init(struct uk_9preq *req);
/**
* Gets the 9p request, incrementing the reference count.
@@ -197,7 +193,8 @@ 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.
+ * If this was the last live reference, it will be placed on the asociated
+ * device's request freelist.
*
* @param req
* Reference to the 9p request.
--
2.26.2
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |