workqueue - queue + drain
Dear all, would you mind helping me understand how workers and workqueues act in a seemingly simple scenario? I'm calling queue_work(my_queue, my_worker) to add a worker to a queue that was created by calling create_singlethread_workqueue(). This goes into queue_work queue_work_on ... if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { __queue_work(cpu, wq, work); ret = true; } ... return false if __queue_work hasn't been called... with static void __queue_work(int cpu, struct workqueue_struct *wq, struct work_struct *work) ... /* if draining, only works from the same workqueue are allowed */ if (unlikely(wq->flags & __WQ_DRAINING) && WARN_ON_ONCE(!is_chained_work(wq))) return; If drain_workqueue(my_queue) is running while queue_work(my_queue, my_worker) is called, my_worker will have WORK_STRUCT_PENDING_BIT set, but it's not queued and no error is returned. With WORK_STRUCT_PENDING_BIT set, all further attempts to queue_work(my_queue, my_worker) later, after draining is done, will fail. This code has been unchanged since at least 4.14. Could anyone shed some light on this, where am I getting things wrong? Thanks and best regards, Martin
On Thursday, August 11, 2022 6:03:51 PM CEST Martin Kaiser wrote:
Dear all,
would you mind helping me understand how workers and workqueues act in a seemingly simple scenario?
Hi Martin, After long time away from this list, today I just saw your email. I soon thought that it's a bit odd that nobody has yet responded, since here many experienced developers for sure know how workqueues work :-) (pun intended). However, I had to read your message several times and I must admit that I'm not yet sure about what you are asking for. Perhaps other readers might have had the very same problem that I experienced, so that they decided to skip your question :-) Therefore, I won't provide plain responses. Instead I'm going to ask you a couple of questions while hoping they may help you to rework your question if they cannot help directly with your question...
I'm calling
queue_work(my_queue, my_worker)
to add a worker to a queue that was created by calling create_singlethread_workqueue().
create_singlethread_workqueue() This is deprecated. Please use alloc_ordered_workqueue(name, WQ_MEM_RECLAIM) from the newer Concurrency Managed Workqueues (CMW).
This goes into
queue_work queue_work_on ... if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { __queue_work(cpu, wq, work); ret = true; } ... return false if __queue_work hasn't been called...
with
static void __queue_work(int cpu, struct workqueue_struct *wq, struct work_struct *work) ... /* if draining, only works from the same workqueue are allowed */ if (unlikely(wq->flags & __WQ_DRAINING) && WARN_ON_ONCE(!is_chained_work(wq))) return;
If drain_workqueue(my_queue) is running while queue_work(my_queue, my_worker) is called, my_worker will have WORK_STRUCT_PENDING_BIT set, but it's not queued and no error is returned.
With WORK_STRUCT_PENDING_BIT set, all further attempts to queue_work(my_queue, my_worker) later, after draining is done, will fail.
Correct, this is what I'd expect to see. I'd be very surprised if it wouldn't fail. Are you aware that, while draining is in progress, only "chain queueing" is allowed? "Chain queuing" means that *only* works already in a draining workqueue are allowed to queue further tasks. Furthermore, flush_workqueue() != drain_workqueue(). Can you explain their different semantics?
This code has been unchanged since at least 4.14. Could anyone shed some light on this, where am I getting things wrong?
Thanks and best regards,
Martin
Regards, Fabio
_______________________________________________ Kernelnewbies mailing list Kernelnewbies@kernelnewbies.org https://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies
participants (2)
-
Fabio M. De Francesco -
Martin Kaiser