Re: [RFC PATCH 01/10] perf workqueue: threadpool creation and destruction
From: Arnaldo Carvalho de Melo <acme@kernel.org>
Date: 2021-07-15 20:48:38
Also in:
lkml
Em Thu, Jul 15, 2021 at 06:31:07PM +0200, Riccardo Mancini escreveu:
Hi Arnaldo, thanks for reviewing the patch! On Wed, 2021-07-14 at 11:16 -0300, Arnaldo Carvalho de Melo wrote: <SNIP>quoted
quoted
+ +enum threadpool_status { + THREADPOOL_STATUS__STOPPED, /* no threads */ + THREADPOOL_STATUS__ERROR, /* errors */ + THREADPOOL_STATUS__MAX +}; + +struct threadpool_struct {Can this be just 'struct threadpool'? I think its descriptive enough:I agree, but I wanted to keep the naming consistent between workqueue.c and threadpool.c.quoted
quoted
+ int nr_threads; /* number of threads in the pool */ + struct thread_struct *threads; /* array of threads in the pool */ + struct task_struct *current_task; /* current executing function */ + enum threadpool_status status; /* current status of the pool */ +}; + +struct thread_struct { + int idx; /* idx of thread in pool-quoted
threads */+ pid_t tid; /* tid of thread */ + struct threadpool_struct *pool; /* parent threadpool */ + struct { + int from[2]; /* messages from thread (acks) */ + int to[2]; /* messages to thread (commands) */ + } pipes; +};This one, since we have already a 'struct thread' in tools/perf, to represent a PERF_RECORD_FORK, perhaps we can call it 'struct threadpool_entry'?Agreed.quoted
quoted
+ +/** + * init_pipes - initialize all pipes of @thread + */ +static void init_pipes(struct thread_struct *thread) +{ + thread->pipes.from[0] = -1; + thread->pipes.from[1] = -1; + thread->pipes.to[0] = -1; + thread->pipes.to[1] = -1; +} + +/** + * open_pipes - open all pipes of @thread + */ +static int open_pipes(struct thread_struct *thread)Here please: threadpool_entry__open_pipes() Its longer, but helps with ctags/cscope navigation and we can go directly to it via: :ta threadpool_entry__open_p<TAB> While 'ta: open_pipes' may bo to various places where this idiom is used.Agreed. <SNIP>quoted
quoted
+/** + * create_threadpool - create a fixed threadpool with @n_threads threads + */ +struct threadpool_struct *create_threadpool(int n_threads)Is this already something the kernel has and thus we should keep the naming? I couldn't find it in the kernel, so please name it: struct threadpool *threadpool__new(int nthreads)As before, I did this to keep consistency with workqueue. Since this threadpool+workqueue can be a standalone library, I preferred to keep the naming consistent inside it, instead of making it consistent with perf (this is what I was referring to in the cover letter, not just the workqueue API). What do you think? I also prefer perf's naming conventions, but it'd feel strange to use two different naming conventions inside the same library.
See my comment on the other message about this naming dilemma :-)
quoted
quoted
+{ + int ret, t; + struct threadpool_struct *pool = malloc(sizeof(*pool)); + + if (!pool) { + pr_err("threadpool: cannot allocate pool: %s\n", + strerror(errno));oHumm, pr_err() at this level isn't appropriate, please make callers complain.ok.quoted
quoted
+ return NULL; + } + + if (n_threads <= 0) { + pr_err("threadpool: invalid number of threads: %d\n", + n_threads);pr_debug()okquoted
quoted
+ goto out_free_pool; + } + + pool->nr_threads = n_threads; + pool->current_task = NULL; + + pool->threads = malloc(n_threads * sizeof(*pool->threads)); + if (!pool->threads) { + pr_err("threadpool: cannot allocate threads: %s\n", + strerror(errno)); + goto out_free_pool; + } + + for (t = 0; t < n_threads; t++) { + pool->threads[t].idx = t; + pool->threads[t].tid = -1; + pool->threads[t].pool = pool; + init_pipes(&pool->threads[t]); + } + + for (t = 0; t < n_threads; t++) { + ret = open_pipes(&pool->threads[t]); + if (ret) + goto out_close_pipes; + } + + pool->status = THREADPOOL_STATUS__STOPPED; + + return pool; + +out_close_pipes: + for (t = 0; t < n_threads; t++) + close_pipes(&pool->threads[t]); + + free(pool->threads); +out_free_pool: + free(pool); + return NULL;Here we can use ERR_PTR()/PTR_ERR() to let the caller know what was the problem, i.e. we can ditch all the pr_err/pr_debug(), etc and instead have a threadpool__strerror(struct threadpool *pool, int err) like we have for 'struct evsel', please take a look at evsel__open_strerror().Thanks, I'll have a look at it. So, what I sould do is not use pr_* higher than debug inside library code and return meaningful errors through PR_ERR, right?
Right.
quoted
quoted
+} + +/** + * destroy_threadpool - free the @pool and all its resources + */ +void destroy_threadpool(struct threadpool_struct *pool)void threadpool__delete(struct threadpool *pool)quoted
+{ + int t; + + if (!pool) + return; + + WARN_ON(pool->status != THREADPOOL_STATUS__STOPPED + && pool->status != THREADPOOL_STATUS__ERROR); + + for (t = 0; t < pool->nr_threads; t++) + close_pipes(&pool->threads[t]);reset pool->threads[t] to -1already inside close_pipes. I agree it might be confusing without the threadpool_entry__ prefix.quoted
quoted
+ + free(pool->threads);zfreeIn general, when should I use zfree instead of free?quoted
quoted
+ free(pool); +} + +/** + * threadpool_size - get number of threads in the threadpool + */ +int threadpool_size(struct threadpool_struct *pool)threadpool__size()ok Thanks, Riccardoquoted
quoted
+{ + return pool->nr_threads; +}diff --git a/tools/perf/util/workqueue/threadpool.hb/tools/perf/util/workqueue/threadpool.h new file mode 100644 index 0000000000000000..2b9388c768a0b588--- /dev/null +++ b/tools/perf/util/workqueue/threadpool.h@@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __WORKQUEUE_THREADPOOL_H +#define __WORKQUEUE_THREADPOOL_H + +struct threadpool_struct; +struct task_struct; + +typedef void (*task_func_t)(int tidx, struct task_struct *task); + +struct task_struct { + task_func_t fn; +}; + +extern struct threadpool_struct *create_threadpool(int n_threads); +extern void destroy_threadpool(struct threadpool_struct *pool); + +extern int threadpool_size(struct threadpool_struct *pool); + +#endif /* __WORKQUEUE_THREADPOOL_H */-- 2.31.1
-- - Arnaldo