diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index f04f425c6c44..f3d9a7252949 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -442,13 +442,6 @@ static int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps, red_flags_changed = table->red_flags != sopt->flags; table->red_flags = sopt->flags; - /* - * Every entry point to GRED is synchronized with the above code - * and the DP is checked against DPs, i.e. shadowed VQs can no - * longer be found so we can unlock right here. - */ - sch_tree_unlock(sch); - if (sopt->grio) { gred_enable_rio_mode(table); gred_disable_wred_mode(table); @@ -474,6 +467,15 @@ static int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps, } } + /* + * Keep the lock until every field the datapath reads (the + * RIO/WRED mode bits, the per-VQ red_flags and the tab[] slots) + * has been updated: gred_enqueue()/gred_dequeue() read them under + * the same lock, so publishing them after the unlock races with + * the enqueue path. + */ + sch_tree_unlock(sch); + gred_offload(sch, TC_GRED_REPLACE); return 0; }