diff options
Diffstat (limited to 'net/core/skmsg.c')
| -rw-r--r-- | net/core/skmsg.c | 82 | 
1 files changed, 39 insertions, 43 deletions
diff --git a/net/core/skmsg.c b/net/core/skmsg.c index f81883759d38..a29508e1ff35 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -481,8 +481,6 @@ int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg,  		msg_rx = sk_psock_peek_msg(psock);  	}  out: -	if (psock->work_state.skb && copied > 0) -		schedule_work(&psock->work);  	return copied;  }  EXPORT_SYMBOL_GPL(sk_msg_recvmsg); @@ -624,42 +622,33 @@ static int sk_psock_handle_skb(struct sk_psock *psock, struct sk_buff *skb,  static void sk_psock_skb_state(struct sk_psock *psock,  			       struct sk_psock_work_state *state, -			       struct sk_buff *skb,  			       int len, int off)  {  	spin_lock_bh(&psock->ingress_lock);  	if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) { -		state->skb = skb;  		state->len = len;  		state->off = off; -	} else { -		sock_drop(psock->sk, skb);  	}  	spin_unlock_bh(&psock->ingress_lock);  }  static void sk_psock_backlog(struct work_struct *work)  { -	struct sk_psock *psock = container_of(work, struct sk_psock, work); +	struct delayed_work *dwork = to_delayed_work(work); +	struct sk_psock *psock = container_of(dwork, struct sk_psock, work);  	struct sk_psock_work_state *state = &psock->work_state;  	struct sk_buff *skb = NULL; +	u32 len = 0, off = 0;  	bool ingress; -	u32 len, off;  	int ret;  	mutex_lock(&psock->work_mutex); -	if (unlikely(state->skb)) { -		spin_lock_bh(&psock->ingress_lock); -		skb = state->skb; +	if (unlikely(state->len)) {  		len = state->len;  		off = state->off; -		state->skb = NULL; -		spin_unlock_bh(&psock->ingress_lock);  	} -	if (skb) -		goto start; -	while ((skb = skb_dequeue(&psock->ingress_skb))) { +	while ((skb = skb_peek(&psock->ingress_skb))) {  		len = skb->len;  		off = 0;  		if (skb_bpf_strparser(skb)) { @@ -668,7 +657,6 @@ static void sk_psock_backlog(struct work_struct *work)  			off = stm->offset;  			len = stm->full_len;  		} -start:  		ingress = skb_bpf_ingress(skb);  		skb_bpf_redirect_clear(skb);  		do { @@ -678,22 +666,28 @@ start:  							  len, ingress);  			if (ret <= 0) {  				if (ret == -EAGAIN) { -					sk_psock_skb_state(psock, state, skb, -							   len, off); +					sk_psock_skb_state(psock, state, len, off); + +					/* Delay slightly to prioritize any +					 * other work that might be here. +					 */ +					if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) +						schedule_delayed_work(&psock->work, 1);  					goto end;  				}  				/* Hard errors break pipe and stop xmit. */  				sk_psock_report_error(psock, ret ? -ret : EPIPE);  				sk_psock_clear_state(psock, SK_PSOCK_TX_ENABLED); -				sock_drop(psock->sk, skb);  				goto end;  			}  			off += ret;  			len -= ret;  		} while (len); -		if (!ingress) +		skb = skb_dequeue(&psock->ingress_skb); +		if (!ingress) {  			kfree_skb(skb); +		}  	}  end:  	mutex_unlock(&psock->work_mutex); @@ -734,7 +728,7 @@ struct sk_psock *sk_psock_init(struct sock *sk, int node)  	INIT_LIST_HEAD(&psock->link);  	spin_lock_init(&psock->link_lock); -	INIT_WORK(&psock->work, sk_psock_backlog); +	INIT_DELAYED_WORK(&psock->work, sk_psock_backlog);  	mutex_init(&psock->work_mutex);  	INIT_LIST_HEAD(&psock->ingress_msg);  	spin_lock_init(&psock->ingress_lock); @@ -786,11 +780,6 @@ static void __sk_psock_zap_ingress(struct sk_psock *psock)  		skb_bpf_redirect_clear(skb);  		sock_drop(psock->sk, skb);  	} -	kfree_skb(psock->work_state.skb); -	/* We null the skb here to ensure that calls to sk_psock_backlog -	 * do not pick up the free'd skb. -	 */ -	psock->work_state.skb = NULL;  	__sk_psock_purge_ingress_msg(psock);  } @@ -809,7 +798,6 @@ void sk_psock_stop(struct sk_psock *psock)  	spin_lock_bh(&psock->ingress_lock);  	sk_psock_clear_state(psock, SK_PSOCK_TX_ENABLED);  	sk_psock_cork_free(psock); -	__sk_psock_zap_ingress(psock);  	spin_unlock_bh(&psock->ingress_lock);  } @@ -823,7 +811,8 @@ static void sk_psock_destroy(struct work_struct *work)  	sk_psock_done_strp(psock); -	cancel_work_sync(&psock->work); +	cancel_delayed_work_sync(&psock->work); +	__sk_psock_zap_ingress(psock);  	mutex_destroy(&psock->work_mutex);  	psock_progs_drop(&psock->progs); @@ -938,7 +927,7 @@ static int sk_psock_skb_redirect(struct sk_psock *from, struct sk_buff *skb)  	}  	skb_queue_tail(&psock_other->ingress_skb, skb); -	schedule_work(&psock_other->work); +	schedule_delayed_work(&psock_other->work, 0);  	spin_unlock_bh(&psock_other->ingress_lock);  	return 0;  } @@ -990,10 +979,8 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb,  		err = -EIO;  		sk_other = psock->sk;  		if (sock_flag(sk_other, SOCK_DEAD) || -		    !sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) { -			skb_bpf_redirect_clear(skb); +		    !sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED))  			goto out_free; -		}  		skb_bpf_set_ingress(skb); @@ -1018,22 +1005,23 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb,  			spin_lock_bh(&psock->ingress_lock);  			if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) {  				skb_queue_tail(&psock->ingress_skb, skb); -				schedule_work(&psock->work); +				schedule_delayed_work(&psock->work, 0);  				err = 0;  			}  			spin_unlock_bh(&psock->ingress_lock); -			if (err < 0) { -				skb_bpf_redirect_clear(skb); +			if (err < 0)  				goto out_free; -			}  		}  		break;  	case __SK_REDIRECT: +		tcp_eat_skb(psock->sk, skb);  		err = sk_psock_skb_redirect(psock, skb);  		break;  	case __SK_DROP:  	default:  out_free: +		skb_bpf_redirect_clear(skb); +		tcp_eat_skb(psock->sk, skb);  		sock_drop(psock->sk, skb);  	} @@ -1049,7 +1037,7 @@ static void sk_psock_write_space(struct sock *sk)  	psock = sk_psock(sk);  	if (likely(psock)) {  		if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) -			schedule_work(&psock->work); +			schedule_delayed_work(&psock->work, 0);  		write_space = psock->saved_write_space;  	}  	rcu_read_unlock(); @@ -1078,8 +1066,7 @@ static void sk_psock_strp_read(struct strparser *strp, struct sk_buff *skb)  		skb_dst_drop(skb);  		skb_bpf_redirect_clear(skb);  		ret = bpf_prog_run_pin_on_cpu(prog, skb); -		if (ret == SK_PASS) -			skb_bpf_set_strparser(skb); +		skb_bpf_set_strparser(skb);  		ret = sk_psock_map_verd(ret, skb_bpf_redirect_fetch(skb));  		skb->sk = NULL;  	} @@ -1183,12 +1170,11 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb)  	int ret = __SK_DROP;  	int len = skb->len; -	skb_get(skb); -  	rcu_read_lock();  	psock = sk_psock(sk);  	if (unlikely(!psock)) {  		len = 0; +		tcp_eat_skb(sk, skb);  		sock_drop(sk, skb);  		goto out;  	} @@ -1212,12 +1198,22 @@ out:  static void sk_psock_verdict_data_ready(struct sock *sk)  {  	struct socket *sock = sk->sk_socket; +	int copied;  	trace_sk_data_ready(sk);  	if (unlikely(!sock || !sock->ops || !sock->ops->read_skb))  		return; -	sock->ops->read_skb(sk, sk_psock_verdict_recv); +	copied = sock->ops->read_skb(sk, sk_psock_verdict_recv); +	if (copied >= 0) { +		struct sk_psock *psock; + +		rcu_read_lock(); +		psock = sk_psock(sk); +		if (psock) +			psock->saved_data_ready(sk); +		rcu_read_unlock(); +	}  }  void sk_psock_start_verdict(struct sock *sk, struct sk_psock *psock)  | 
