@@ -364,6 +364,9 @@ struct tcp_diag_md5sig {
#define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */
#define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */
#define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */
+#define TCP_AO_CMDF_DEL_ASYNC (1 << 3) /* Asynchronious delete, valid
+ * only for listen sockets
+ */
#define TCP_AO_GET_CURR TCP_AO_CMDF_CURR
#define TCP_AO_GET_NEXT TCP_AO_CMDF_NEXT
@@ -1504,7 +1504,7 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd,
#define TCP_AO_CMDF_ADDMOD_VALID \
(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP)
#define TCP_AO_CMDF_DEL_VALID \
- (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)
+ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_DEL_ASYNC)
#define TCP_AO_GETF_VALID \
(TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT)
@@ -1633,11 +1633,26 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_key *key,
hlist_del_rcu(&key->node);
+ /* Support for async delete on listening sockets: as they don't
+ * need current_key/rnext_key maintaining, we don't need to check
+ * them and we can just free all resources in RCU fashion.
+ */
+ if (cmd->tcpa_flags & TCP_AO_CMDF_DEL_ASYNC) {
+ if (sk->sk_state != TCP_LISTEN)
+ return -EINVAL;
+ atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
+ call_rcu(&key->rcu, tcp_ao_key_free_rcu);
+ return 0;
+ }
+
/* At this moment another CPU could have looked this key up
* while it was unlinked from the list. Wait for RCU grace period,
* after which the key is off-list and can't be looked up again;
* the rx path [just before RCU came] might have used it and set it
* as current_key (very unlikely).
+ * Free the key with next RCU grace period (in case it was
+ * current_key before tcp_ao_current_rnext() might have
+ * changed it in forced-delete).
*/
synchronize_rcu();
err = tcp_ao_current_rnext(sk, cmd->tcpa_flags,