Merge remote-tracking branch 'net-next/master'
[linux] / net / core / skmsg.c
index 8c82660..ae6f06e 100644 (file)
@@ -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)