Skip to content

Commit b3fd8fa

Browse files
DaanDeMeyerkeszybz
authored andcommitted
network/tc: Avoid concurrent set modification in tclass_drop()/qdisc_drop()
With the current algorithm, we can end up removing entries from the qdisc/tclass sets while having multiple open iterators over the sets at various positions which leads to assertion failures in the hashmap logic as it's only safe to remove the "current" entry. To avoid the problem, let's split up marking and dropping of tclasses and qdiscs. First, we recursively iterate tclasses/qdiscs and mark all that need to be removed. Next, we iterate once over tclasses and qdiscs and remove all marked entries. Fixes 632d321 (cherry picked from commit ee8f605)
1 parent 0530cf3 commit b3fd8fa

File tree

4 files changed

+77
-34
lines changed

4 files changed

+77
-34
lines changed

src/network/tc/qdisc.c

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -285,37 +285,57 @@ int link_find_qdisc(Link *link, uint32_t handle, const char *kind, QDisc **ret)
285285
return -ENOENT;
286286
}
287287

288-
QDisc* qdisc_drop(QDisc *qdisc) {
288+
void qdisc_mark_recursive(QDisc *qdisc) {
289289
TClass *tclass;
290-
Link *link;
291290

292291
assert(qdisc);
292+
assert(qdisc->link);
293293

294-
link = ASSERT_PTR(qdisc->link);
294+
if (qdisc_is_marked(qdisc))
295+
return;
295296

296-
qdisc_mark(qdisc); /* To avoid stack overflow. */
297+
qdisc_mark(qdisc);
297298

298-
/* also drop all child classes assigned to the qdisc. */
299-
SET_FOREACH(tclass, link->tclasses) {
300-
if (tclass_is_marked(tclass))
299+
/* also mark all child classes assigned to the qdisc. */
300+
SET_FOREACH(tclass, qdisc->link->tclasses) {
301+
if (TC_H_MAJ(tclass->classid) != qdisc->handle)
301302
continue;
302303

303-
if (TC_H_MAJ(tclass->classid) != qdisc->handle)
304+
tclass_mark_recursive(tclass);
305+
}
306+
}
307+
308+
void link_qdisc_drop_marked(Link *link) {
309+
QDisc *qdisc;
310+
311+
assert(link);
312+
313+
SET_FOREACH(qdisc, link->qdiscs) {
314+
if (!qdisc_is_marked(qdisc))
304315
continue;
305316

306-
tclass_drop(tclass);
317+
qdisc_unmark(qdisc);
318+
qdisc_enter_removed(qdisc);
319+
320+
if (qdisc->state == 0) {
321+
log_qdisc_debug(qdisc, link, "Forgetting");
322+
qdisc_free(qdisc);
323+
} else
324+
log_qdisc_debug(qdisc, link, "Removed");
307325
}
326+
}
308327

309-
qdisc_unmark(qdisc);
310-
qdisc_enter_removed(qdisc);
328+
QDisc* qdisc_drop(QDisc *qdisc) {
329+
assert(qdisc);
330+
assert(qdisc->link);
311331

312-
if (qdisc->state == 0) {
313-
log_qdisc_debug(qdisc, link, "Forgetting");
314-
qdisc = qdisc_free(qdisc);
315-
} else
316-
log_qdisc_debug(qdisc, link, "Removed");
332+
qdisc_mark_recursive(qdisc);
333+
334+
/* link_qdisc_drop_marked() may invalidate qdisc, so run link_tclass_drop_marked() first. */
335+
link_tclass_drop_marked(qdisc->link);
336+
link_qdisc_drop_marked(qdisc->link);
317337

318-
return qdisc;
338+
return NULL;
319339
}
320340

321341
static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, QDisc *qdisc) {

src/network/tc/qdisc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(QDisc, qdisc);
7777
QDisc* qdisc_free(QDisc *qdisc);
7878
int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret);
7979

80+
void qdisc_mark_recursive(QDisc *qdisc);
8081
QDisc* qdisc_drop(QDisc *qdisc);
82+
void link_qdisc_drop_marked(Link *link);
8183

8284
int link_find_qdisc(Link *link, uint32_t handle, const char *kind, QDisc **qdisc);
8385

src/network/tc/tclass.c

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -252,37 +252,56 @@ static void log_tclass_debug(TClass *tclass, Link *link, const char *str) {
252252
strna(tclass_get_tca_kind(tclass)));
253253
}
254254

255-
TClass* tclass_drop(TClass *tclass) {
255+
void tclass_mark_recursive(TClass *tclass) {
256256
QDisc *qdisc;
257-
Link *link;
258257

259258
assert(tclass);
259+
assert(tclass->link);
260260

261-
link = ASSERT_PTR(tclass->link);
261+
if (tclass_is_marked(tclass))
262+
return;
262263

263-
tclass_mark(tclass); /* To avoid stack overflow. */
264+
tclass_mark(tclass);
264265

265-
/* Also drop all child qdiscs assigned to the class. */
266-
SET_FOREACH(qdisc, link->qdiscs) {
267-
if (qdisc_is_marked(qdisc))
266+
/* Also mark all child qdiscs assigned to the class. */
267+
SET_FOREACH(qdisc, tclass->link->qdiscs) {
268+
if (qdisc->parent != tclass->classid)
268269
continue;
269270

270-
if (qdisc->parent != tclass->classid)
271+
qdisc_mark_recursive(qdisc);
272+
}
273+
}
274+
275+
void link_tclass_drop_marked(Link *link) {
276+
TClass *tclass;
277+
278+
assert(link);
279+
280+
SET_FOREACH(tclass, link->tclasses) {
281+
if (!tclass_is_marked(tclass))
271282
continue;
272283

273-
qdisc_drop(qdisc);
284+
tclass_unmark(tclass);
285+
tclass_enter_removed(tclass);
286+
287+
if (tclass->state == 0) {
288+
log_tclass_debug(tclass, link, "Forgetting");
289+
tclass_free(tclass);
290+
} else
291+
log_tclass_debug(tclass, link, "Removed");
274292
}
293+
}
275294

276-
tclass_unmark(tclass);
277-
tclass_enter_removed(tclass);
295+
TClass* tclass_drop(TClass *tclass) {
296+
assert(tclass);
278297

279-
if (tclass->state == 0) {
280-
log_tclass_debug(tclass, link, "Forgetting");
281-
tclass = tclass_free(tclass);
282-
} else
283-
log_tclass_debug(tclass, link, "Removed");
298+
tclass_mark_recursive(tclass);
299+
300+
/* link_tclass_drop_marked() may invalidate tclass, so run link_qdisc_drop_marked() first. */
301+
link_qdisc_drop_marked(tclass->link);
302+
link_tclass_drop_marked(tclass->link);
284303

285-
return tclass;
304+
return NULL;
286305
}
287306

288307
static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, TClass *tclass) {

src/network/tc/tclass.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(TClass, tclass);
5858
TClass* tclass_free(TClass *tclass);
5959
int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
6060

61+
void tclass_mark_recursive(TClass *tclass);
6162
TClass* tclass_drop(TClass *tclass);
63+
void link_tclass_drop_marked(Link *link);
6264

6365
int link_find_tclass(Link *link, uint32_t classid, TClass **ret);
6466

0 commit comments

Comments
 (0)