From: Jens Axboe <[email protected]>
To: Stefan Roesch <[email protected]>,
[email protected], [email protected]
Cc: [email protected],
Olivier Langlois <[email protected]>,
Jakub Kicinski <[email protected]>
Subject: Re: [PATCH v8 4/7] io-uring: add napi busy poll support
Date: Thu, 9 Feb 2023 17:00:43 -0700 [thread overview]
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
I'd fold 2+3 into this patch again, having them standalone don't really
make a lot of sense without this patch.
This looks a lot better and gets rid of the ifdef infestation! Minor
comments below, mostly just because I think we should fold those 3
patches anyway.
> diff --git a/io_uring/napi.c b/io_uring/napi.c
> new file mode 100644
> index 000000000000..c9e2afae382d
> --- /dev/null
> +++ b/io_uring/napi.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include "io_uring.h"
> +#include "napi.h"
> +
> +#ifdef CONFIG_NET_RX_BUSY_POLL
> +
> +/* Timeout for cleanout of stale entries. */
> +#define NAPI_TIMEOUT (60 * SEC_CONVERSION)
> +
> +struct io_napi_ht_entry {
> + unsigned int napi_id;
> + struct list_head list;
> +
> + /* Covered by napi lock spinlock. */
> + unsigned long timeout;
> + struct hlist_node node;
> +};
> +
> +static inline bool io_napi_busy_loop_on(struct io_ring_ctx *ctx)
> +{
> + return READ_ONCE(ctx->napi_busy_poll_to);
> +}
I'd probably get rid of this helper, to be honest.
> +static bool io_napi_busy_loop(struct list_head *napi_list, bool prefer_busy_poll)
> +{
> + struct io_napi_ht_entry *e;
> + struct io_napi_ht_entry *n;
> +
> + list_for_each_entry_safe(e, n, napi_list, list) {
> + napi_busy_loop(e->napi_id, NULL, NULL, prefer_busy_poll,
> + BUSY_POLL_BUDGET);
> + }
Looks like 8 spaces before that BUSY_POLL_BUDGET, should be a tab?
> +static void io_napi_blocking_busy_loop(struct list_head *napi_list,
> + struct io_wait_queue *iowq)
> +{
> + unsigned long start_time = 0;
> +
> + if (!list_is_singular(napi_list))
> + start_time = busy_loop_current_time();
> +
> + while (!list_is_singular(napi_list) &&
> + io_napi_busy_loop(napi_list, iowq->napi_prefer_busy_poll) &&
> + !io_napi_busy_loop_should_end(iowq, start_time)) {
> + ;
> + }
> +
> + if (list_is_singular(napi_list)) {
> + struct io_napi_ht_entry *ne = list_first_entry(napi_list,
> + struct io_napi_ht_entry, list);
> +
> + napi_busy_loop(ne->napi_id, io_napi_busy_loop_should_end, iowq,
> + iowq->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
> + }
> +}
This does look a LOT better! I do think a helper for the first while
would make sense, and then have a comment in that helper on what this is
doing exactly.
static void io_napi_multi_busy_loop(napi_list, iowq)
{
unsigned long start_time = busy_loop_current_time();
do {
if (list_is_singular(napi_list))
break;
if (!io_napi_busy_loop(napi_list, iowq->napi_prefer_busy_poll))
break;
} while (!io_napi_busy_loop_should_end(iowq, start_time));
}
static void io_napi_blocking_busy_loop(struct list_head *napi_list,
struct io_wait_queue *iowq)
{
if (!list_is_singular(napi_list))
io_napi_multi_busy_loop(napi_list, iowq);
if (list_is_singular(napi_list)) {
struct io_napi_ht_entry *ne;
ne = list_first_entry(napi_list, struct io_napi_ht_entry, list);
napi_busy_loop(ne->napi_id, io_napi_busy_loop_should_end, iowq,
iowq->napi_prefer_busy_poll, BUSY_POLL_BUDGET);
}
}
I think that is still much easier to read rather than all of these
combined statements. What do you think?
> +static void io_napi_merge_lists(struct io_ring_ctx *ctx, struct list_head *napi_list)
> +{
> + spin_lock(&ctx->napi_lock);
> + list_splice(napi_list, &ctx->napi_list);
> + io_napi_remove_stale(ctx);
> + spin_unlock(&ctx->napi_lock);
> +}
First line too long, split it into two. Did you look into the locking
side like I mentioned in the previous review?
> +/*
> + * io_napi_adjust_busy_loop_timeout() - Add napi id to the busy poll list
> + * @ctx: pointer to io-uring context structure
> + * @iowq: pointer to io wait queue
> + * @napi_list: pointer to head of napi list
> + * @ts: pointer to timespec or NULL
> + *
> + * Adjust the busy loop timeout according to timespec and busy poll timeout.
> + */
> +void io_napi_adjust_busy_loop_timeout(struct io_ring_ctx *ctx,
> + struct io_wait_queue *iowq,
> + struct list_head *napi_list,
> + struct timespec64 *ts)
> +{
> + if (!list_empty(napi_list)) {
> + if (ts)
> + __io_napi_adjust_busy_loop_timeout(
> + READ_ONCE(ctx->napi_busy_poll_to),
> + ts, &iowq->napi_busy_poll_to);
> + else
> + iowq->napi_busy_poll_to = READ_ONCE(ctx->napi_busy_poll_to);
> + }
> +}
I'd make this:
void io_napi_adjust_timeout(struct io_ring_ctx *ctx, struct io_wait_queue *iowq,
struct list_head *napi_list, struct timespec64 *ts)
{
if (list_empty(napi_list))
return;
__io_napi_adjust_timeout(ctx, iowq, napi_list, ts);
}
and put it in the header. That leaves the fast path mostly untouched,
rather than forcing a function call here.
Also note the alignment of the variables in the function header, this
applies in a bunch of spots. And just drop the busy_loop thing from the
naming where it isn't strictly needed, lots of these function names are
really long.
> +/*
> + * io_napi_setup_busy_loop() - setup the busy poll loop
> + * @ctx: pointer to io-uring context structure
> + * @iowq: pointer to io wait queue
> + * @napi_list: pointer to head of napi list
> + *
> + * Capture busy poll timeout and prefer busy poll seeting Splice of the napi list.
> + */
> +void io_napi_setup_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq,
> + struct list_head *napi_list)
> +{
> + iowq->napi_busy_poll_to = 0;
> + iowq->napi_prefer_busy_poll = READ_ONCE(ctx->napi_prefer_busy_poll);
> +
> + if (!(ctx->flags & IORING_SETUP_SQPOLL)) {
> + spin_lock(&ctx->napi_lock);
> + list_splice_init(&ctx->napi_list, napi_list);
> + spin_unlock(&ctx->napi_lock);
> + }
> +}
Might need a comment here on why SQPOLL needs something extra?
> +/*
> + * io_napi_end_busy_loop() - execute busy poll loop
> + * @ctx: pointer to io-uring context structure
> + * @iowq: pointer to io wait queue
> + * @napi_list: pointer to head of napi list
> + *
> + * Execute the busy poll loop and merge the spliced off list.
> + */
> +void io_napi_end_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq,
> + struct list_head *napi_list)
> +{
> + if (iowq->napi_busy_poll_to)
> + io_napi_blocking_busy_loop(napi_list, iowq);
> +
> + if (!list_empty(napi_list))
> + io_napi_merge_lists(ctx, napi_list);
> +}
This should go above the users in this file. Maybe others are like that
too, didn't check.
> +++ b/io_uring/napi.h
> @@ -0,0 +1,49 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef IOU_NAPI_H
> +#define IOU_NAPI_H
> +
> +#include <linux/kernel.h>
> +#include <linux/io_uring.h>
> +#include <net/busy_poll.h>
> +
> +#ifdef CONFIG_NET_RX_BUSY_POLL
> +
> +#define NAPI_LIST_HEAD(l) LIST_HEAD(l)
> +
> +void io_napi_init(struct io_ring_ctx *ctx);
> +void io_napi_free(struct io_ring_ctx *ctx);
> +
> +void io_napi_add(struct io_kiocb *req);
> +
> +void io_napi_setup_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq,
> + struct list_head *napi_list);
> +void io_napi_adjust_busy_loop_timeout(struct io_ring_ctx *ctx,
> + struct io_wait_queue *iowq, struct list_head *napi_list,
> + struct timespec64 *ts);
> +void io_napi_end_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq,
> + struct list_head *napi_list);
> +
> +#else
> +
> +#define NAPI_LIST_HEAD(l)
> +
> +static inline void io_napi_init(struct io_ring_ctx *ctx)
> +{
> +}
> +
> +static inline void io_napi_free(struct io_ring_ctx *ctx)
> +{
> +}
> +
> +static inline void io_napi_add(struct io_kiocb *req)
> +{
> +}
> +
> +#define io_napi_setup_busy_loop(ctx, iowq, napi_list) do {} while (0)
> +#define io_napi_adjust_busy_loop_timeout(ctx, iowq, napi_list, ts) do {} while (0)
> +#define io_napi_end_busy_loop(ctx, iowq, napi_list) do {} while (0)
This looks way better!
--
Jens Axboe
next prev parent reply other threads:[~2023-02-10 0:00 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-09 23:01 [PATCH v8 0/7] io_uring: add napi busy polling support Stefan Roesch
2023-02-09 23:01 ` [PATCH v8 1/7] io-uring: move io_wait_queue definition to header file Stefan Roesch
2023-02-09 23:01 ` [PATCH v8 2/7] io-uring: add napi fields to io_ring_ctx Stefan Roesch
2023-02-09 23:01 ` [PATCH v8 3/7] io-uring: add busy poll timeout, prefer busy poll to io_wait_queue Stefan Roesch
2023-02-09 23:01 ` [PATCH v8 4/7] io-uring: add napi busy poll support Stefan Roesch
2023-02-10 0:00 ` Jens Axboe [this message]
2023-02-15 17:22 ` Stefan Roesch
2023-02-09 23:01 ` [PATCH v8 5/7] io-uring: add sqpoll support for napi busy poll Stefan Roesch
2023-02-10 0:14 ` Jens Axboe
2023-02-09 23:01 ` [PATCH v8 6/7] io_uring: add api to set / get napi configuration Stefan Roesch
2023-02-10 0:02 ` Jens Axboe
2023-02-09 23:01 ` [PATCH v8 7/7] " Stefan Roesch
2023-02-10 0:02 ` Jens Axboe
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 \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
/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