History

workqueue: Mark barrier work with WORK_STRUCT_INACTIVE

Currently, WORK_NO_COLOR has two meanings: Not participate in flushing Not participate in nr_active And only non-barrier work items are marked with WORK_STRUCT_INACTIVE when they are in inactive_works list. The barrier work items are not marked INACTIVE even linked in inactive_works list since these tail items are always moved together with the head work item. These definitions are simple, clean and practical. (Except a small blemish that only the first meaning of WORK_NO_COLOR is documented in include/linux/workqueue.h while both meanings are in workqueue.c) But dual-purpose WORK_NO_COLOR used for barrier work items has proven to be problematical[1]. Only the second purpose is obligatory. So we plan to make barrier work items participate in flushing but keep them still not participating in nr_active. So the plan is to mark barrier work items inactive without using WORK_NO_COLOR in this patch so that we can assign a flushing color to them in next patch. The reasonable way is to add or reuse a bit in work data of the work item. But adding a bit will double the size of pool_workqueue. Currently, WORK_STRUCT_INACTIVE is only used in try_to_grab_pending() for user-queued work items and try_to_grab_pending() can't work for barrier work items. So we extend WORK_STRUCT_INACTIVE to also mark barrier work items no matter which list they are in because we don't need to determind which list a barrier work item is in. So the meaning of WORK_STRUCT_INACTIVE becomes just "the work items don't participate in nr_active" (no matter whether it is a barrier work item or a user-queued work item). And WORK_STRUCT_INACTIVE for user-queued work items means they are in inactive_works list. This patch does it by setting WORK_STRUCT_INACTIVE for barrier work items in insert_wq_barrier() and checking WORK_STRUCT_INACTIVE first in pwq_dec_nr_in_flight(). And the meaning of WORK_NO_COLOR is reduced to only "not participating in flushing". There is no functionality change intended in this patch. Because WORK_NO_COLOR+WORK_STRUCT_INACTIVE represents the previous WORK_NO_COLOR in meaning and try_to_grab_pending() doesn't use for barrier work items and avoids being confused by this extended WORK_STRUCT_INACTIVE. A bunch of comment for nr_active & WORK_STRUCT_INACTIVE is also added for documenting how WORK_STRUCT_INACTIVE works in nr_active management. [1]: https://lore.kernel.org/lkml/20210812083814.32453-1-lizhe.67@bytedance.com/ Signed-off-by: Lai Jiangshan <laijs@linux.alibaba.com> Signed-off-by: Tejun Heo <tj@kernel.org>

Lai Jiangshan committed 5 years ago

Commit 018f3a11 parent d21cece1 file · +34 −8Browse files

Showing the changes to kernel/workqueue.c (1 of 1 file). Show all

Unified · Split

kernel/workqueue.c+34−8
@@ -205,6 +205,23 @@ struct pool_workqueue { · 204 unmodified lines
205 int refcnt; /* L: reference count */205 int refcnt; /* L: reference count */
206 int nr_in_flight[WORK_NR_COLORS];206 int nr_in_flight[WORK_NR_COLORS];
207 /* L: nr of in_flight works */207 /* L: nr of in_flight works */
208
209 /*
210 * nr_active management and WORK_STRUCT_INACTIVE:
211 *
212 * When pwq->nr_active >= max_active, new work item is queued to
213 * pwq->inactive_works instead of pool->worklist and marked with
214 * WORK_STRUCT_INACTIVE.
215 *
216 * All work items marked with WORK_STRUCT_INACTIVE do not participate
217 * in pwq->nr_active and all work items in pwq->inactive_works are
218 * marked with WORK_STRUCT_INACTIVE. But not all WORK_STRUCT_INACTIVE
219 * work items are in pwq->inactive_works. Some of them are ready to
220 * run in pool->worklist or worker->scheduled. Those work itmes are
221 * only struct wq_barrier which is used for flush_work() and should
222 * not participate in pwq->nr_active. For non-barrier work item, it
223 * is marked with WORK_STRUCT_INACTIVE iff it is in pwq->inactive_works.
224 */
208 int nr_active; /* L: nr of active works */225 int nr_active; /* L: nr of active works */
209 int max_active; /* L: max active works */226 int max_active; /* L: max active works */
210 struct list_head inactive_works; /* L: inactive works */227 struct list_head inactive_works; /* L: inactive works */
@@ -1171,19 +1188,21 @@ static void pwq_dec_nr_in_flight(struct pool_workqueue *pwq, unsigned long work_ · 960 unmodified lines
1171{1188{
1172 int color = get_work_color(work_data);1189 int color = get_work_color(work_data);
1173 1190
1174 /* uncolored work items don't participate in flushing or nr_active */1191 if (!(work_data & WORK_STRUCT_INACTIVE)) {
1192 pwq->nr_active--;
1193 if (!list_empty(&pwq->inactive_works)) {
1194 /* one down, submit an inactive one */
1195 if (pwq->nr_active < pwq->max_active)
1196 pwq_activate_first_inactive(pwq);
1197 }
1198 }
1199
1200 /* uncolored work items don't participate in flushing */
1175 if (color == WORK_NO_COLOR)1201 if (color == WORK_NO_COLOR)
1176 goto out_put;1202 goto out_put;
1177 1203
1178 pwq->nr_in_flight[color]--;1204 pwq->nr_in_flight[color]--;
1179 1205
1180 pwq->nr_active--;
1181 if (!list_empty(&pwq->inactive_works)) {
1182 /* one down, submit an inactive one */
1183 if (pwq->nr_active < pwq->max_active)
1184 pwq_activate_first_inactive(pwq);
1185 }
1186
1187 /* is flush in progress and are we at the flushing tip? */1206 /* is flush in progress and are we at the flushing tip? */
1188 if (likely(pwq->flush_color != color))1207 if (likely(pwq->flush_color != color))
1189 goto out_put;1208 goto out_put;
@@ -1283,6 +1302,10 @@ static int try_to_grab_pending(struct work_struct *work, bool is_dwork, · 93 unmodified lines
1283 debug_work_deactivate(work);1302 debug_work_deactivate(work);
1284 1303
1285 /*1304 /*
1305 * A cancelable inactive work item must be in the
1306 * pwq->inactive_works since a queued barrier can't be
1307 * canceled (see the comments in insert_wq_barrier()).
1308 *
1286 * An inactive work item cannot be grabbed directly because1309 * An inactive work item cannot be grabbed directly because
1287 * it might have linked NO_COLOR work items which, if left1310 * it might have linked NO_COLOR work items which, if left
1288 * on the inactive_works list, will confuse pwq->nr_active1311 * on the inactive_works list, will confuse pwq->nr_active
@@ -2675,6 +2698,9 @@ static void insert_wq_barrier(struct pool_workqueue *pwq, · 1386 unmodified lines
2675 2698
2676 barr->task = current;2699 barr->task = current;
2677 2700
2701 /* The barrier work item does not participate in pwq->nr_active. */
2702 work_flags |= WORK_STRUCT_INACTIVE;
2703
2678 /*2704 /*
2679 * If @target is currently being executed, schedule the2705 * If @target is currently being executed, schedule the
2680 * barrier to the worker; otherwise, put it after @target.2706 * barrier to the worker; otherwise, put it after @target.