diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-02-25 22:56:40 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-02-25 22:58:42 +0100 |
commit | 4c25956de55aefc96f16ddc5324c8a304b570aff (patch) | |
tree | 620efc40874f29a50f0a308556e8b39db5327c6a /src/peer.c | |
parent | 0e4996e2c4fd414ef1554911a6401f353312829a (diff) |
peer: only synchronize_rcu_bh and traverse trie once when removing all peers
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'src/peer.c')
-rw-r--r-- | src/peer.c | 61 |
1 files changed, 42 insertions, 19 deletions
@@ -87,16 +87,8 @@ struct wg_peer *wg_peer_get_maybe_zero(struct wg_peer *peer) return peer; } -/* We have a separate "remove" function make sure that all active places where - * a peer is currently operating will eventually come to an end and not pass - * their reference onto another context. - */ -void wg_peer_remove(struct wg_peer *peer) +static void peer_make_dead(struct wg_peer *peer) { - if (unlikely(!peer)) - return; - lockdep_assert_held(&peer->device->device_update_lock); - /* Remove from configuration-time lookup structures. */ list_del_init(&peer->peer_list); wg_allowedips_remove_by_peer(&peer->device->peer_allowedips, peer, @@ -105,7 +97,13 @@ void wg_peer_remove(struct wg_peer *peer) /* Mark as dead, so that we don't allow jumping contexts after. */ WRITE_ONCE(peer->is_dead, true); - synchronize_rcu_bh(); + + /* The caller must now synchronize_rcu_bh() for this to take effect. */ +} + +static void peer_remove_after_dead(struct wg_peer *peer) +{ + WARN_ON(!peer->is_dead); /* No more keypairs can be created for this peer, since is_dead protects * add_new_keypair, so we can now destroy existing ones. @@ -161,6 +159,40 @@ void wg_peer_remove(struct wg_peer *peer) wg_peer_put(peer); } +/* We have a separate "remove" function make sure that all active places where + * a peer is currently operating will eventually come to an end and not pass + * their reference onto another context. + */ +void wg_peer_remove(struct wg_peer *peer) +{ + if (unlikely(!peer)) + return; + lockdep_assert_held(&peer->device->device_update_lock); + + peer_make_dead(peer); + synchronize_rcu_bh(); + peer_remove_after_dead(peer); +} + +void wg_peer_remove_all(struct wg_device *wg) +{ + struct list_head dead_peers = LIST_HEAD_INIT(dead_peers); + struct wg_peer *peer, *temp; + + lockdep_assert_held(&wg->device_update_lock); + + /* Avoid having to traverse individually for each one. */ + wg_allowedips_free(&wg->peer_allowedips, &wg->device_update_lock); + + list_for_each_entry_safe(peer, temp, &wg->peer_list, peer_list) { + peer_make_dead(peer); + list_add_tail(&peer->peer_list, &dead_peers); + } + synchronize_rcu_bh(); + list_for_each_entry_safe(peer, temp, &dead_peers, peer_list) + peer_remove_after_dead(peer); +} + static void rcu_release(struct rcu_head *rcu) { struct wg_peer *peer = container_of(rcu, struct wg_peer, rcu); @@ -204,12 +236,3 @@ void wg_peer_put(struct wg_peer *peer) return; kref_put(&peer->refcount, kref_release); } - -void wg_peer_remove_all(struct wg_device *wg) -{ - struct wg_peer *peer, *temp; - - lockdep_assert_held(&wg->device_update_lock); - list_for_each_entry_safe(peer, temp, &wg->peer_list, peer_list) - wg_peer_remove(peer); -} |