mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Kent Overstreet <koverstreet@google.com>
To: linux-kernel@vger.kernel.org, linux-aio@kvack.org,
	linux-fsdevel@vger.kernel.org
Cc: zab@redhat.com, bcrl@kvack.org, jmoyer@redhat.com,
	axboe@kernel.dk, viro@zeniv.linux.org.uk,
	Kent Overstreet <koverstreet@google.com>
Subject: [PATCH 22/26] aio: percpu reqs_available
Date: Mon,  3 Dec 2012 12:58:38 -0800	[thread overview]
Message-ID: <1354568322-29029-23-git-send-email-koverstreet@google.com> (raw)
In-Reply-To: <1354568322-29029-1-git-send-email-koverstreet@google.com>

See the previous patch for why we want to do this - this basically
implements a per cpu allocator for reqs_available that doesn't actually
allocate anything.

Note that we need to increase the size of the ringbuffer we allocate,
since a single thread won't necessarily be able to use all the
reqs_available slots - some (up to about half) might be on other per cpu
lists, unavailable for the current thread.

We size the ringbuffer based on the nr_events userspace passed to
io_setup(), so this is a slight behaviour change - but nr_events wasn't
being used as a hard limit before, it was being rounded up to the next
page before so this doesn't change the actual semantics.

Signed-off-by: Kent Overstreet <koverstreet@google.com>
---
 fs/aio.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 86 insertions(+), 13 deletions(-)

diff --git a/fs/aio.c b/fs/aio.c
index 182aa21..bde360d 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -26,6 +26,7 @@
 #include <linux/mm.h>
 #include <linux/mman.h>
 #include <linux/mmu_context.h>
+#include <linux/percpu.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
 #include <linux/aio.h>
@@ -59,6 +60,10 @@ struct aio_ring {
 
 #define AIO_RING_PAGES	8
 
+struct kioctx_cpu {
+	unsigned		reqs_available;
+};
+
 struct kioctx {
 	atomic_t		users;
 	atomic_t		dead;
@@ -67,6 +72,10 @@ struct kioctx {
 	unsigned long		user_id;
 	struct hlist_node	list;
 
+	struct __percpu kioctx_cpu *cpu;
+
+	unsigned		req_batch;
+
 	unsigned		nr;
 
 	/* sys_io_setup currently limits this to an unsigned int */
@@ -152,6 +161,9 @@ static int aio_setup_ring(struct kioctx *ctx)
 	unsigned long size;
 	int nr_pages;
 
+	nr_events = max(nr_events, num_possible_cpus() * 4);
+	nr_events *= 2;
+
 	/* Compensate for the ring buffer's head/tail overlap entry */
 	nr_events += 2;	/* 1 is required, 2 for good luck */
 
@@ -258,6 +270,8 @@ static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb,
 static void free_ioctx_rcu(struct rcu_head *head)
 {
 	struct kioctx *ctx = container_of(head, struct kioctx, rcu_head);
+
+	free_percpu(ctx->cpu);
 	kmem_cache_free(kioctx_cachep, ctx);
 }
 
@@ -271,7 +285,7 @@ static void free_ioctx(struct kioctx *ctx)
 	struct aio_ring *ring;
 	struct io_event res;
 	struct kiocb *iocb;
-	unsigned head, avail;
+	unsigned cpu, head, avail;
 
 	spin_lock_irq(&ctx->ctx_lock);
 
@@ -285,6 +299,13 @@ static void free_ioctx(struct kioctx *ctx)
 
 	spin_unlock_irq(&ctx->ctx_lock);
 
+	for_each_possible_cpu(cpu) {
+		struct kioctx_cpu *kcpu = per_cpu_ptr(ctx->cpu, cpu);
+
+		atomic_add(kcpu->reqs_available, &ctx->reqs_available);
+		kcpu->reqs_available = 0;
+	}
+
 	ring = kmap_atomic(ctx->ring_pages[0]);
 	head = ring->head;
 	kunmap_atomic(ring);
@@ -360,10 +381,16 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 
 	INIT_LIST_HEAD(&ctx->active_reqs);
 
-	if (aio_setup_ring(ctx) < 0)
+	ctx->cpu = alloc_percpu(struct kioctx_cpu);
+	if (!ctx->cpu)
 		goto out_freectx;
 
+	if (aio_setup_ring(ctx) < 0)
+		goto out_freepcpu;
+
 	atomic_set(&ctx->reqs_available, ctx->nr);
+	ctx->req_batch = ctx->nr / (num_possible_cpus() * 4);
+	BUG_ON(!ctx->req_batch);
 
 	/* limit the number of system wide aios */
 	spin_lock(&aio_nr_lock);
@@ -387,6 +414,8 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 out_cleanup:
 	err = -EAGAIN;
 	aio_free_ring(ctx);
+out_freepcpu:
+	free_percpu(ctx->cpu);
 out_freectx:
 	kmem_cache_free(kioctx_cachep, ctx);
 	pr_debug("error allocating ioctx %d\n", err);
@@ -485,6 +514,52 @@ void exit_aio(struct mm_struct *mm)
 	}
 }
 
+static void put_reqs_available(struct kioctx *ctx, unsigned nr)
+{
+	struct kioctx_cpu *kcpu;
+
+	preempt_disable();
+	kcpu = this_cpu_ptr(ctx->cpu);
+
+	kcpu->reqs_available += nr;
+	while (kcpu->reqs_available >= ctx->req_batch * 2) {
+		kcpu->reqs_available -= ctx->req_batch;
+		atomic_add(ctx->req_batch, &ctx->reqs_available);
+	}
+
+	preempt_enable();
+}
+
+static bool get_reqs_available(struct kioctx *ctx)
+{
+	struct kioctx_cpu *kcpu;
+	bool ret = false;
+
+	preempt_disable();
+	kcpu = this_cpu_ptr(ctx->cpu);
+
+	if (!kcpu->reqs_available) {
+		int old, avail = atomic_read(&ctx->reqs_available);
+
+		do {
+			if (avail < ctx->req_batch)
+				goto out;
+
+			old = avail;
+			avail = atomic_cmpxchg(&ctx->reqs_available,
+					       avail, avail - ctx->req_batch);
+		} while (avail != old);
+
+		kcpu->reqs_available += ctx->req_batch;
+	}
+
+	ret = true;
+	kcpu->reqs_available--;
+out:
+	preempt_enable();
+	return ret;
+}
+
 /* aio_get_req
  *	Allocate a slot for an aio request.  Increments the ki_users count
  * of the kioctx so that the kioctx stays around until all requests are
@@ -499,7 +574,7 @@ static inline struct kiocb *aio_get_req(struct kioctx *ctx)
 {
 	struct kiocb *req;
 
-	if (atomic_dec_if_positive(&ctx->reqs_available) <= 0)
+	if (!get_reqs_available(ctx))
 		return NULL;
 
 	req = kmem_cache_alloc(kiocb_cachep, GFP_KERNEL|__GFP_ZERO);
@@ -508,10 +583,9 @@ static inline struct kiocb *aio_get_req(struct kioctx *ctx)
 
 	atomic_set(&req->ki_users, 2);
 	req->ki_ctx = ctx;
-
 	return req;
 out_put:
-	atomic_inc(&ctx->reqs_available);
+	put_reqs_available(ctx, 1);
 	return NULL;
 }
 
@@ -601,6 +675,10 @@ void aio_complete(struct kiocb *iocb, long res, long res2)
 	 * when the event got cancelled.
 	 */
 	if (xchg(&iocb->ki_cancel, KIOCB_CANCELLED) == KIOCB_CANCELLED) {
+		/*
+		 * Can't use the percpu reqs_available here - could race with
+		 * free_ioctx()
+		 */
 		atomic_inc(&ctx->reqs_available);
 		/* Still need the wake_up in case free_ioctx is waiting */
 		goto put_rq;
@@ -725,7 +803,7 @@ static int aio_read_events(struct kioctx *ctx, struct io_event __user *event,
 	kunmap_atomic(ring);
 	flush_dcache_page(ctx->ring_pages[0]);
 
-	atomic_add(ret, &ctx->reqs_available);
+	put_reqs_available(ctx, ret);
 
 	pr_debug("%d  h%u t%u\n", ret, *head, ctx->tail);
 
@@ -791,12 +869,7 @@ retry:
 		}
 
 		mutex_unlock(&ctx->ring_lock);
-
-		/* Try to only show up in io wait if there are ops in flight */
-		if (atomic_read(&ctx->reqs_available) != ctx->nr)
-			io_schedule();
-		else
-			schedule();
+		io_schedule();
 
 		if (signal_pending(current)) {
 			ret = -EINTR;
@@ -1185,7 +1258,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
 	return 0;
 
 out_put_req:
-	atomic_inc(&ctx->reqs_available);
+	put_reqs_available(ctx, 1);
 	aio_put_req(req);	/* drop extra ref to req */
 	aio_put_req(req);	/* drop i/o ref to req */
 	return ret;
-- 
1.7.12


  parent reply	other threads:[~2012-12-03 21:02 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-12-03 20:58 [PATCH 00/26] AIO performance improvements/cleanups, v2 Kent Overstreet
2012-12-03 20:58 ` [PATCH 01/26] mm: remove old aio use_mm() comment Kent Overstreet
2012-12-03 20:58 ` [PATCH 02/26] aio: remove dead code from aio.h Kent Overstreet
2012-12-03 20:58 ` [PATCH 03/26] gadget: remove only user of aio retry Kent Overstreet
2012-12-03 20:58 ` [PATCH 04/26] aio: remove retry-based AIO Kent Overstreet
2012-12-27 10:11   ` Fubo Chen
2012-12-03 20:58 ` [PATCH 05/26] char: add aio_{read,write} to /dev/{null,zero} Kent Overstreet
2012-12-03 20:58 ` [PATCH 06/26] aio: Kill return value of aio_complete() Kent Overstreet
2012-12-03 20:58 ` [PATCH 07/26] aio: kiocb_cancel() Kent Overstreet
2012-12-03 20:58 ` [PATCH 08/26] aio: Move private stuff out of aio.h Kent Overstreet
2012-12-03 20:58 ` [PATCH 09/26] aio: dprintk() -> pr_debug() Kent Overstreet
2012-12-03 20:58 ` [PATCH 10/26] aio: do fget() after aio_get_req() Kent Overstreet
2012-12-03 20:58 ` [PATCH 11/26] aio: Make aio_put_req() lockless Kent Overstreet
2012-12-03 20:58 ` [PATCH 12/26] aio: Refcounting cleanup Kent Overstreet
2012-12-03 20:58 ` [PATCH 13/26] aio: Convert read_events() to hrtimers Kent Overstreet
2012-12-03 20:58 ` [PATCH 14/26] aio: Make aio_read_evt() more efficient Kent Overstreet
2012-12-03 20:58 ` [PATCH 15/26] aio: Use flush_dcache_page() Kent Overstreet
2012-12-03 20:58 ` [PATCH 16/26] aio: Use cancellation list lazily Kent Overstreet
2012-12-03 20:58 ` [PATCH 17/26] aio: Change reqs_active to include unreaped completions Kent Overstreet
2012-12-03 20:58 ` [PATCH 18/26] aio: Kill batch allocation Kent Overstreet
2012-12-03 20:58 ` [PATCH 19/26] aio: Kill struct aio_ring_info Kent Overstreet
2012-12-03 20:58 ` [PATCH 20/26] aio: Give shared kioctx fields their own cachelines Kent Overstreet
2012-12-03 20:58 ` [PATCH 21/26] aio: reqs_active -> reqs_available Kent Overstreet
2012-12-03 20:58 ` Kent Overstreet [this message]
2012-12-03 20:58 ` [PATCH 23/26] Generic dynamic per cpu refcounting Kent Overstreet
2012-12-27 13:47   ` Fubo Chen
2012-12-03 20:58 ` [PATCH 24/26] aio: Percpu ioctx refcount Kent Overstreet
2012-12-03 20:58 ` [PATCH 25/26] aio: use xchg() instead of completion_lock Kent Overstreet
2012-12-03 20:58 ` [PATCH 26/26] aio: Don't include aio.h in sched.h Kent Overstreet
2012-12-13 21:18 ` [PATCH 00/26] AIO performance improvements/cleanups, v2 Jens Axboe
2012-12-14  2:26   ` Jack Wang
2012-12-14  7:35     ` Jens Axboe
2012-12-15  9:25       ` Kent Overstreet
2012-12-15  9:46         ` Jens Axboe
2012-12-15 10:36           ` Kent Overstreet
2012-12-15 12:59             ` Jens Axboe
2012-12-15 13:16             ` Jens Axboe
2012-12-18 19:16               ` Kent Overstreet
2012-12-19  6:45                 ` Kent Overstreet

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1354568322-29029-23-git-send-email-koverstreet@google.com \
    --to=koverstreet@google.com \
    --cc=axboe@kernel.dk \
    --cc=bcrl@kvack.org \
    --cc=jmoyer@redhat.com \
    --cc=linux-aio@kvack.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=zab@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®