Before this change state creating sequence was: 1) lock wire key hash 2) link state's wire key 3) unlock wire key hash 4) lock stack key hash 5) link state's stack key 6) unlock stack key hash 7) lock ID hash 8) link into ID hash 9) unlock ID hash What could happen here is that other thread finds the state via key hash lookup after 6), locks ID hash and does some processing of the state. When the thread creating state unblocks, it finds the state it was inserting already non-virgin. Now we perform proper interlocking between key hash locks and ID hash lock: 1) lock wire & stack hashes 2) link state's keys 3) lock ID hash 4) unlock wire & stack hashes 5) link into ID hash 6) unlock ID hash To achieve that, the following hacking was performed in pf_state_key_attach(): - Key hash mutex is marked with MTX_DUPOK. - To avoid deadlock on 2 key hash mutexes, we lock them in order determined by their address value. - pf_state_key_attach() had a magic to reuse a > FIN_WAIT_2 state. It unlinked the conflicting state synchronously. In theory this could require locking a third key hash, which we can't do now. Now we do not remove the state immediately, instead we leave this task to the purge thread. To avoid conflicts in a short period before state is purged, we push to the very end of the TAILQ. - On success, before dropping key hash locks, pf_state_key_attach() locks ID hash and returns. Tested by: Ian FREISLICH <ianf clue.co.za> |
||
---|---|---|
.. | ||
if_pflog.c | ||
if_pfsync.c | ||
in4_cksum.c | ||
pf_if.c | ||
pf_ioctl.c | ||
pf_lb.c | ||
pf_norm.c | ||
pf_osfp.c | ||
pf_ruleset.c | ||
pf_table.c | ||
pf.c |