@@ -985,6 +985,7 @@ EXPORT_SYMBOL_GPL(sk_psock_tls_strp_read);
static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb,
int verdict)
{
+ struct sk_psock_work_state *state;
struct sock *sk_other;
int err = 0;
u32 len, off;
@@ -1001,13 +1002,28 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb,
skb_bpf_set_ingress(skb);
+ /* We need to grab mutex here because in-flight skb is in one of
+ * the following states: either on ingress_skb, in psock->state
+ * or being processed by backlog and neither in state->skb and
+ * ingress_skb may be also empty. The troublesome case is when
+ * the skb has been dequeued from ingress_skb list or taken from
+ * state->skb because we can not easily test this case. Maybe we
+ * could be clever with flags and resolve this but being clever
+ * got us here in the first place and we note this is done under
+ * sock lock and backlog conditions mean we are already running
+ * into ENOMEM or other performance hindering cases so lets do
+ * the obvious thing and grab the mutex.
+ */
+ mutex_lock(&psock->work_mutex);
+ state = &psock->work_state;
+
/* If the queue is empty then we can submit directly
* into the msg queue. If its not empty we have to
* queue work otherwise we may get OOO data. Otherwise,
* if sk_psock_skb_ingress errors will be handled by
* retrying later from workqueue.
*/
- if (skb_queue_empty(&psock->ingress_skb)) {
+ if (skb_queue_empty(&psock->ingress_skb) && likely(!state->skb)) {
len = skb->len;
off = 0;
if (skb_bpf_strparser(skb)) {
@@ -1028,9 +1044,11 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb,
spin_unlock_bh(&psock->ingress_lock);
if (err < 0) {
skb_bpf_redirect_clear(skb);
+ mutex_unlock(&psock->work_mutex);
goto out_free;
}
}
+ mutex_unlock(&psock->work_mutex);
break;
case __SK_REDIRECT:
err = sk_psock_skb_redirect(psock, skb);