cancel_delayed_work() can't be called from IRQ handlers due to its use of del_timer_sync() and can't cancel work items which are already transferred from timer to worklist. Also, unlike other flush and cancel functions, a canceled delayed_work would still point to the last associated cpu_workqueue. If the workqueue is destroyed afterwards and the work item is re-used on a different workqueue, the queueing code can oops trying to dereference already freed cpu_workqueue. This patch reimplements cancel_delayed_work() using try_to_grab_pending() and set_work_cpu_and_clear_pending(). This allows the function to be called from IRQ handlers and makes its behavior consistent with other flush / cancel functions.
Showing the changes to kernel/workqueue.c (1 of 2 files). Show all
Unified · Split
| @@ -2948,6 +2948,36 @@ bool flush_delayed_work(struct delayed_work *dwork) · 2947 unmodified lines | ||
| 2948 | 2948 | } |
| 2949 | 2949 | EXPORT_SYMBOL(flush_delayed_work); |
| 2950 | 2950 | |
| 2951 | +/** | |
| 2952 | + * cancel_delayed_work - cancel a delayed work | |
| 2953 | + * @dwork: delayed_work to cancel | |
| 2954 | + * | |
| 2955 | + * Kill off a pending delayed_work. Returns %true if @dwork was pending | |
| 2956 | + * and canceled; %false if wasn't pending. Note that the work callback | |
| 2957 | + * function may still be running on return, unless it returns %true and the | |
| 2958 | + * work doesn't re-arm itself. Explicitly flush or use | |
| 2959 | + * cancel_delayed_work_sync() to wait on it. | |
| 2960 | + * | |
| 2961 | + * This function is safe to call from any context including IRQ handler. | |
| 2962 | + */ | |
| 2963 | +bool cancel_delayed_work(struct delayed_work *dwork) | |
| 2964 | +{ | |
| 2965 | + unsigned long flags; | |
| 2966 | + int ret; | |
| 2967 | + | |
| 2968 | + do { | |
| 2969 | + ret = try_to_grab_pending(&dwork->work, true, &flags); | |
| 2970 | + } while (unlikely(ret == -EAGAIN)); | |
| 2971 | + | |
| 2972 | + if (unlikely(ret < 0)) | |
| 2973 | + return false; | |
| 2974 | + | |
| 2975 | + set_work_cpu_and_clear_pending(&dwork->work, work_cpu(&dwork->work)); | |
| 2976 | + local_irq_restore(flags); | |
| 2977 | + return true; | |
| 2978 | +} | |
| 2979 | +EXPORT_SYMBOL(cancel_delayed_work); | |
| 2980 | + | |
| 2951 | 2981 | /** |
| 2952 | 2982 | * cancel_delayed_work_sync - cancel a delayed work and wait for it to finish |
| 2953 | 2983 | * @dwork: the delayed work cancel |