DF-2849 / fix.diff
--- a/sys/kern/subr_gtaskqueue.c +++ b/sys/kern/subr_gtaskqueue.c @@ -200,6 +200,12 @@ int grouptaskqueue_enqueue(struct gtaskqueue *queue, struct gtask *gtask) { + /* + * A detached grouptask has gt_taskqueue == NULL; fail gracefully + * instead of panicking (INVARIANTS) or faulting on TQ_LOCK(NULL). + */ + if (queue == NULL) + return (EINVAL); #ifdef INVARIANTS if (queue == NULL) { gtask_dump(gtask); @@ -659,6 +665,15 @@ qgroup->tqg_queue[qid].tgc_cnt++; LIST_INSERT_HEAD(&qgroup->tqg_queue[qid].tgc_tasks, gtask, gt_list); gtask->gt_taskqueue = qgroup->tqg_queue[qid].tgc_taskq; + /* re-arm the task under the queue lock (detach leaves + * TASK_NOENQUEUE set) so the flag RMW cannot race enqueue */ + { + struct gtaskqueue *gtq = gtask->gt_taskqueue; + + TQ_LOCK(gtq); + gtask->gt_task.ta_flags &= ~TASK_NOENQUEUE; + TQ_UNLOCK(gtq); + } if (dev != NULL && irq != NULL) { cpu = qgroup->tqg_queue[qid].tgc_cpu; gtask->gt_cpu = cpu; @@ -707,6 +722,15 @@ qgroup->tqg_queue[qid].tgc_cnt++; LIST_INSERT_HEAD(&qgroup->tqg_queue[qid].tgc_tasks, gtask, gt_list); gtask->gt_taskqueue = qgroup->tqg_queue[qid].tgc_taskq; + /* re-arm the task under the queue lock (detach leaves + * TASK_NOENQUEUE set) so the flag RMW cannot race enqueue */ + { + struct gtaskqueue *gtq = gtask->gt_taskqueue; + + TQ_LOCK(gtq); + gtask->gt_task.ta_flags &= ~TASK_NOENQUEUE; + TQ_UNLOCK(gtq); + } cpu = qgroup->tqg_queue[qid].tgc_cpu; lockmgr(&qgroup->tqg_lock, LK_RELEASE); @@ -744,7 +768,12 @@ LIST_REMOVE(gtask, gt_list); lockmgr(&qgroup->tqg_lock, LK_RELEASE); gtask->gt_taskqueue = NULL; - gtask->gt_task.ta_flags &= ~TASK_NOENQUEUE; + /* + * Leave TASK_NOENQUEUE set while the task is detached: a racing + * grouptaskqueue_enqueue() fails with EAGAIN (stale queue pointer) + * or EINVAL (NULL queue) instead of corrupting the queue list or + * panicking. attach re-arms the flag under the queue lock. + */ } static void |