[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [UNIKRAFT PATCH v2 8/9] lib/uk9p: Recycle uk_9preq allocations
Reviewed-by: Costin Lupu <costin.lupu@xxxxxxxxx> On 5/9/20 7:44 PM, Cristian Banu wrote: > 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.8%) > 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(): > > 500281499 uk_9p_trace_request_create > 500281570 uk_9p_trace_request_allocated > 500281603 uk_9p_trace_ready tag 0 > 500283423 uk_9p_trace_sent tag 0 > 500347243 uk_9p_trace_received tag 0 > > Signed-off-by: Cristian Banu <cristb@xxxxxxxxx> > --- > lib/uk9p/9pdev.c | 80 +++++++++++++++++++++++++++++--- > 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, 95 insertions(+), 26 deletions(-) > > diff --git a/lib/uk9p/9pdev.c b/lib/uk9p/9pdev.c > index 40a4daa..bdd6a84 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,37 @@ 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 +416,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. >
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |