From: Carlos Maiolino <cem@kernel.org>
To: "Darrick J. Wong" <djwong@kernel.org>
Cc: linux-xfs@vger.kernel.org
Subject: Re: [PATCH 1/6] libfrog: fix overly sleep workqueues
Date: Wed, 13 Sep 2023 14:41:21 +0200 [thread overview]
Message-ID: <20230913124121.sz6keo7p73ps57g4@andromeda> (raw)
In-Reply-To: <169454758152.3539425.17620295149533266267.stgit@frogsfrogsfrogs>
On Tue, Sep 12, 2023 at 12:39:41PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> I discovered the following bad behavior in the workqueue code when I
> noticed that xfs_scrub was running single-threaded despite having 4
> virtual CPUs allocated to the VM. I observed this sequence:
>
> Thread 1 WQ1 WQ2...N
> workqueue_create
> <start up>
> pthread_cond_wait
> <start up>
> pthread_cond_wait
> workqueue_add
> next_item == NULL
> pthread_cond_signal
>
> workqueue_add
> next_item != NULL
> <do not pthread_cond_signal>
>
> <receives wakeup>
> <run first item>
>
> workqueue_add
> next_item != NULL
> <do not pthread_cond_signal>
>
> <run second item>
> <run third item>
> pthread_cond_wait
>
> workqueue_terminate
> pthread_cond_broadcast
> <receives wakeup>
> <nothing to do, exits>
> <wakes up again>
> <nothing to do, exits>
>
> Notice how threads WQ2...N are completely idle while WQ1 ends up doing
> all the work! That wasn't the point of a worker pool! Observe that
> thread 1 manages to queue two work items before WQ1 pulls the first item
> off the queue. When thread 1 queues the third item, it sees that
> next_item is not NULL, so it doesn't wake a worker. If thread 1 queues
> all the N work that it has before WQ1 empties the queue, then none of
> the other thread get woken up.
>
> Fix this by maintaining a count of the number of active threads, and
> using that to wake either the sole idle thread, or all the threads if
> there are many that are idle. This dramatically improves startup
> behavior of the workqueue and eliminates the collapse case.
>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> ---
> libfrog/workqueue.c | 34 ++++++++++++++++++++++++----------
> libfrog/workqueue.h | 1 +
> 2 files changed, 25 insertions(+), 10 deletions(-)
>
>
> diff --git a/libfrog/workqueue.c b/libfrog/workqueue.c
> index 702a53e2f3c..db5b3f68bc5 100644
> --- a/libfrog/workqueue.c
> +++ b/libfrog/workqueue.c
> @@ -26,8 +26,8 @@ workqueue_thread(void *arg)
> * Check for notification to exit after every chunk of work.
> */
> rcu_register_thread();
> + pthread_mutex_lock(&wq->lock);
> while (1) {
> - pthread_mutex_lock(&wq->lock);
>
> /*
> * Wait for work.
> @@ -36,10 +36,8 @@ workqueue_thread(void *arg)
> assert(wq->item_count == 0);
> pthread_cond_wait(&wq->wakeup, &wq->lock);
> }
> - if (wq->next_item == NULL && wq->terminate) {
> - pthread_mutex_unlock(&wq->lock);
> + if (wq->next_item == NULL && wq->terminate)
> break;
> - }
>
> /*
> * Dequeue work from the head of the list. If the queue was
> @@ -57,11 +55,16 @@ workqueue_thread(void *arg)
> /* more work, wake up another worker */
> pthread_cond_signal(&wq->wakeup);
> }
> + wq->active_threads++;
> pthread_mutex_unlock(&wq->lock);
>
> (wi->function)(wi->queue, wi->index, wi->arg);
> free(wi);
> +
> + pthread_mutex_lock(&wq->lock);
> + wq->active_threads--;
> }
> + pthread_mutex_unlock(&wq->lock);
> rcu_unregister_thread();
>
> return NULL;
> @@ -170,12 +173,6 @@ workqueue_add(
> restart:
> if (wq->next_item == NULL) {
> assert(wq->item_count == 0);
> - ret = -pthread_cond_signal(&wq->wakeup);
> - if (ret) {
> - pthread_mutex_unlock(&wq->lock);
> - free(wi);
> - return ret;
> - }
> wq->next_item = wi;
> } else {
> /* throttle on a full queue if configured */
> @@ -192,6 +189,23 @@ workqueue_add(
> }
> wq->last_item = wi;
> wq->item_count++;
> +
> + if (wq->active_threads == wq->thread_count - 1) {
> + /* One thread is idle, wake it */
> + ret = -pthread_cond_signal(&wq->wakeup);
> + if (ret) {
> + pthread_mutex_unlock(&wq->lock);
> + return ret;
> + }
> + } else if (wq->active_threads < wq->thread_count) {
> + /* Multiple threads are idle, wake everyone */
> + ret = -pthread_cond_broadcast(&wq->wakeup);
> + if (ret) {
> + pthread_mutex_unlock(&wq->lock);
> + return ret;
> + }
> + }
> +
> pthread_mutex_unlock(&wq->lock);
>
> return 0;
> diff --git a/libfrog/workqueue.h b/libfrog/workqueue.h
> index a9c108d0e66..edbe12fabab 100644
> --- a/libfrog/workqueue.h
> +++ b/libfrog/workqueue.h
> @@ -29,6 +29,7 @@ struct workqueue {
> pthread_cond_t wakeup;
> unsigned int item_count;
> unsigned int thread_count;
> + unsigned int active_threads;
> bool terminate;
> bool terminated;
> int max_queued;
>
next prev parent reply other threads:[~2023-09-13 12:41 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-12 19:39 [PATCHSET 0/6] xfsprogs: minor fixes Darrick J. Wong
2023-09-12 19:39 ` [PATCH 1/6] libfrog: fix overly sleep workqueues Darrick J. Wong
2023-09-13 12:41 ` Carlos Maiolino [this message]
2023-10-05 12:34 ` Carlos Maiolino
2023-09-12 19:39 ` [PATCH 2/6] libfrog: don't fail on XFS_FSOP_GEOM_FLAGS_NREXT64 in xfrog_bulkstat_single5 Darrick J. Wong
2023-09-13 12:50 ` Carlos Maiolino
2023-10-05 12:34 ` Carlos Maiolino
2023-09-12 19:39 ` [PATCH 3/6] libxfs: use XFS_IGET_CREATE when creating new files Darrick J. Wong
2023-09-13 12:58 ` Carlos Maiolino
2023-09-14 18:24 ` Bill O'Donnell
2023-10-05 12:35 ` Carlos Maiolino
2023-09-12 19:39 ` [PATCH 4/6] xfs_scrub: actually return errno from check_xattr_ns_names Darrick J. Wong
2023-09-13 13:00 ` Carlos Maiolino
2023-10-05 12:36 ` Carlos Maiolino
2023-09-12 19:40 ` [PATCH 5/6] xfs_repair: set aformat and anextents correctly when clearing the attr fork Darrick J. Wong
2023-09-13 13:02 ` Carlos Maiolino
2023-09-14 18:25 ` Bill O'Donnell
2023-10-05 12:37 ` Carlos Maiolino
2023-09-12 19:40 ` [PATCH 6/6] libxfs: fix atomic64_t detection on x86 32-bit architectures Darrick J. Wong
2023-09-12 19:47 ` [PATCH v1.1 " Darrick J. Wong
2023-09-13 13:22 ` Carlos Maiolino
2023-09-14 18:26 ` Bill O'Donnell
2023-10-05 12:45 ` Carlos Maiolino
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=20230913124121.sz6keo7p73ps57g4@andromeda \
--to=cem@kernel.org \
--cc=djwong@kernel.org \
--cc=linux-xfs@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.