Of course, it is permitted to lock m_peer_mutex while locking a peer-specific mutex, but I don't see a use-case for that (unless one would want to micro-optimize to avoid taking a copy of a shared pointer). So I think it is better to avoid holding m_peer_mutex longer than needed and only hold it longer when there is a strong rationale.
To extend on this: Another place that would be safer by taking the GetAllPeers snapshot and releasing the m_peer_mutex early is RelayAddress/PushAddress. With the current code in master, the following (arguably absurd) diff would compile fine, but lead to runtime-UB/crash later on:
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 71cc6ff7ba..5a8f6105f0 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1139,2 +1139,3 @@ void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr)
} else {
+ LOCK(m_peer_mutex);
peer.m_addrs_to_send.push_back(addr);
I think copying shared pointers here is fine and beneficial to reduce the (recursive) scope of the lock. Though, this style-cleanup can be done in the future when that code is touched the next time.
edit: nvm, this is caught by clang:
src/net_processing.cpp:1140:10: warning: calling function 'MaybeCheckNotHeld' requires negative capability '!m_peer_mutex' [-Wthread-safety-analysis]
1140 | LOCK(m_peer_mutex);
| ^