diff options
Diffstat (limited to 'net/core/skmsg.c')
| -rw-r--r-- | net/core/skmsg.c | 28 | 
1 files changed, 18 insertions, 10 deletions
diff --git a/net/core/skmsg.c b/net/core/skmsg.c index d6d5c20d7044..ae6f06e45737 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -78,11 +78,9 @@ int sk_msg_clone(struct sock *sk, struct sk_msg *dst, struct sk_msg *src,  {  	int i = src->sg.start;  	struct scatterlist *sge = sk_msg_elem(src, i); +	struct scatterlist *sgd = NULL;  	u32 sge_len, sge_off; -	if (sk_msg_full(dst)) -		return -ENOSPC; -  	while (off) {  		if (sge->length > off)  			break; @@ -94,16 +92,27 @@ int sk_msg_clone(struct sock *sk, struct sk_msg *dst, struct sk_msg *src,  	}  	while (len) { -		if (sk_msg_full(dst)) -			return -ENOSPC; -  		sge_len = sge->length - off; -		sge_off = sge->offset + off;  		if (sge_len > len)  			sge_len = len; + +		if (dst->sg.end) +			sgd = sk_msg_elem(dst, dst->sg.end - 1); + +		if (sgd && +		    (sg_page(sge) == sg_page(sgd)) && +		    (sg_virt(sge) + off == sg_virt(sgd) + sgd->length)) { +			sgd->length += sge_len; +			dst->sg.size += sge_len; +		} else if (!sk_msg_full(dst)) { +			sge_off = sge->offset + off; +			sk_msg_page_add(dst, sg_page(sge), sge_len, sge_off); +		} else { +			return -ENOSPC; +		} +  		off = 0;  		len -= sge_len; -		sk_msg_page_add(dst, sg_page(sge), sge_len, sge_off);  		sk_mem_charge(sk, sge_len);  		sk_msg_iter_var_next(i);  		if (i == src->sg.end && len) @@ -545,8 +554,7 @@ static void sk_psock_destroy_deferred(struct work_struct *gc)  	struct sk_psock *psock = container_of(gc, struct sk_psock, gc);  	/* No sk_callback_lock since already detached. */ -	if (psock->parser.enabled) -		strp_done(&psock->parser.strp); +	strp_done(&psock->parser.strp);  	cancel_work_sync(&psock->work);  |