4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
23 * This file is part of Lustre, http://www.lustre.org/
25 * lustre/utils/lctl_thread.c
27 * Author: Rajeev Mishra <rajeevm@hpe.com>
33 #include "lctl_thread.h"
35 #include <libcfs/util/string.h>
38 * Initialize the given set_param work queue.
40 * \param[out] wq the work queue to initialize
41 * \param[in] popt the options passed to set_param
43 * \retval 0 if successful
44 * \retval -errno if unsuccessful
46 int spwq_init(struct sp_workq *wq, struct param_opts *popt)
51 memset(wq, 0, sizeof(*wq));
54 /* pthread_mutex_init returns 0 for success, or errno for failure */
55 return -pthread_mutex_init(&wq->spwq_mutex, NULL);
59 * Destroy and free space used by a set_param work queue.
61 * \param[in] wq the work queue to destroy
63 * \retval 0 if successful
64 * \retval -errno if unsuccessful
66 int spwq_destroy(struct sp_workq *wq)
76 for (i = 0; i < wq->spwq_len; i++) {
77 free(wq->spwq_items[i].spwi_path);
78 free(wq->spwq_items[i].spwi_param_name);
79 /* wq->spwq_items[i].spwi_value was not malloc'd */
84 /* pthread_mutex_destroy returns 0 for success, or errno for failure */
85 rc = -pthread_mutex_destroy(&wq->spwq_mutex);
87 memset(wq, 0, sizeof(*wq));
93 * Expand the size of a work queue to fit the requested number of items.
95 * \param[in,out] wq the work queue to expand
96 * \param[in] num_items the number of items to make room for in \a wq
98 * \retval 0 if successful
99 * \retval -errno if unsuccessful
101 int spwq_expand(struct sp_workq *wq, size_t num_items)
105 struct sp_work_item *tmp;
110 space = wq->spwq_size - wq->spwq_len;
112 /* First check if there's already enough room. */
113 if (space >= num_items)
116 new_size = wq->spwq_len + num_items;
118 /* When spwq_items is NULL, realloc behaves like malloc */
119 tmp = realloc(wq->spwq_items, new_size * sizeof(struct sp_work_item));
124 wq->spwq_items = tmp;
125 wq->spwq_size = new_size;
131 * Add an item to a set_param work queue. Not thread-safe.
133 * \param[in,out] wq the work queue to which the item should be added
134 * \param[in] path the full path to the parameter file (will be copied)
135 * \param[in] param_name the name of the parameter (will be copied)
136 * \param[in] value the value for the parameter (will not be copied)
138 * \retval 0 if successful
139 * \retval -errno if unsuccessful
141 int spwq_add_item(struct sp_workq *wq, char *path,
142 char *param_name, char *value)
145 char *param_name_copy;
148 if (!(wq && path && param_name && value))
151 /* Hopefully the caller has expanded the work queue before calling this
152 * function, but make sure there's room just in case.
154 rc = spwq_expand(wq, 1);
158 path_copy = strdup(path);
162 param_name_copy = strdup(param_name);
163 if (!param_name_copy) {
168 wq->spwq_items[wq->spwq_len].spwi_param_name = param_name_copy;
169 wq->spwq_items[wq->spwq_len].spwi_path = path_copy;
170 wq->spwq_items[wq->spwq_len].spwi_value = value;
178 * Gets the next item from the set_param \a wq in a thread-safe manner.
180 * \param[in] wq the workq from which to obtain the next item
181 * \param[out] wi the next work item in \a wa, will be set to NULL if \wq empty
183 * \retval 0 if successful (empty work queue is considered successful)
184 * \retval -errno if unsuccessful
186 static int spwq_next_item(struct sp_workq *wq, struct sp_work_item **wi)
196 rc_lock = pthread_mutex_lock(&wq->spwq_mutex);
198 if (wq->spwq_cur_index < wq->spwq_len)
199 *wi = &wq->spwq_items[wq->spwq_cur_index++];
200 rc_unlock = pthread_mutex_unlock(&wq->spwq_mutex);
203 return rc_lock != 0 ? -rc_lock : -rc_unlock;
207 * A set_param worker thread which sets params from the workq.
209 * \param[in] arg a pointer to a struct sp_workq
211 * \retval 0 if successful
212 * \retval -errno if unsuccessful
214 static void *sp_thread(void *arg)
216 struct sp_workq *wq = (struct sp_workq *)arg;
217 struct param_opts *popt = wq->spwq_popt;
218 struct sp_work_item *work_item;
221 rc = spwq_next_item(wq, &work_item);
226 char *path = work_item->spwi_path;
227 char *param_name = work_item->spwi_param_name;
228 char *value = work_item->spwi_value;
231 rc2 = write_param(path, param_name, popt, value);
234 rc2 = spwq_next_item(wq, &work_item);
243 * Spawn threads and set parameters in a work queue in parallel.
245 * \param[in] wq the work queue containing parameters to set
247 * \retval 0 if successful
248 * \retval -errno if unsuccessful
250 int sp_run_threads(struct sp_workq *wq)
256 pthread_t *sp_threads;
261 if (wq->spwq_len == 0)
264 num_threads = wq->spwq_popt->po_parallel_threads;
265 if (num_threads > wq->spwq_len)
266 num_threads = wq->spwq_len;
268 sp_threads = malloc(sizeof(pthread_t) * num_threads);
272 for (i = 0; i < num_threads; i++) {
273 rc = -pthread_create(&sp_threads[i], NULL, &sp_thread, wq);
278 /* check if we failed to create any threads at all */
282 /* ignore thread creation errors if at least one was created */
285 for (j = 0; j < i; j++) {
289 join_rc = -pthread_join(sp_threads[j], &res);
290 if (join_rc && rc == 0)
293 /* this error takes priority over join errors */
302 #define popt_is_parallel(popt) 0
303 #define spwq_init(wq, popt) 0
304 #define spwq_expand(wq, num_items) 0
305 #define spwq_add_item(wq, path, param_name, value) 0
306 #define sp_run_threads(wq) 0
307 #define spwq_destroy(wq) 0
308 struct sp_workq { int unused; }
309 #endif /* HAVE_LIBPTHREAD */