]> Pileus Git - ~andy/linux/blobdiff - net/tipc/link.c
tipc: Fix sk_buff leaks when link congestion is detected
[~andy/linux] / net / tipc / link.c
index ebf338f7b14e7651b57e788fbac1a5057d6a135d..2a9f44a203ebf71210ff95cc8c64b23a842cc7da 100644 (file)
@@ -864,8 +864,9 @@ int tipc_link_send_buf(struct link *l_ptr, struct sk_buff *buf)
 
        if (unlikely(queue_size >= queue_limit)) {
                if (imp <= TIPC_CRITICAL_IMPORTANCE) {
-                       return link_schedule_port(l_ptr, msg_origport(msg),
-                                                 size);
+                       link_schedule_port(l_ptr, msg_origport(msg), size);
+                       buf_discard(buf);
+                       return -ELINKCONG;
                }
                buf_discard(buf);
                if (imp > CONN_MANAGER) {
@@ -1069,8 +1070,6 @@ again:
                        if (likely(buf)) {
                                res = link_send_buf_fast(l_ptr, buf,
                                                         &sender->max_pkt);
-                               if (unlikely(res < 0))
-                                       buf_discard(buf);
 exit:
                                tipc_node_unlock(node);
                                read_unlock_bh(&tipc_net_lock);
@@ -1169,7 +1168,6 @@ again:
 
        tipc_msg_init(&fragm_hdr, MSG_FRAGMENTER, FIRST_FRAGMENT,
                 INT_H_SIZE, msg_destnode(hdr));
-       msg_set_link_selector(&fragm_hdr, sender->ref);
        msg_set_size(&fragm_hdr, max_pkt);
        msg_set_fragm_no(&fragm_hdr, 1);
 
@@ -1277,7 +1275,7 @@ reject:
        /* Append whole chain to send queue: */
 
        buf = buf_chain;
-       l_ptr->long_msg_seq_no = mod(l_ptr->long_msg_seq_no + 1);
+       l_ptr->long_msg_seq_no++;
        if (!l_ptr->next_out)
                l_ptr->next_out = buf_chain;
        l_ptr->stats.sent_fragmented++;
@@ -2407,6 +2405,8 @@ void tipc_link_recv_bundle(struct sk_buff *buf)
  */
 static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf)
 {
+       struct sk_buff *buf_chain = NULL;
+       struct sk_buff *buf_chain_tail = (struct sk_buff *)&buf_chain;
        struct tipc_msg *inmsg = buf_msg(buf);
        struct tipc_msg fragm_hdr;
        u32 insize = msg_size(inmsg);
@@ -2415,7 +2415,7 @@ static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf)
        u32 rest = insize;
        u32 pack_sz = l_ptr->max_pkt;
        u32 fragm_sz = pack_sz - INT_H_SIZE;
-       u32 fragm_no = 1;
+       u32 fragm_no = 0;
        u32 destaddr;
 
        if (msg_short(inmsg))
@@ -2427,10 +2427,6 @@ static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf)
 
        tipc_msg_init(&fragm_hdr, MSG_FRAGMENTER, FIRST_FRAGMENT,
                 INT_H_SIZE, destaddr);
-       msg_set_link_selector(&fragm_hdr, msg_link_selector(inmsg));
-       msg_set_long_msgno(&fragm_hdr, mod(l_ptr->long_msg_seq_no++));
-       msg_set_fragm_no(&fragm_hdr, fragm_no);
-       l_ptr->stats.sent_fragmented++;
 
        /* Chop up message: */
 
@@ -2443,27 +2439,37 @@ static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf)
                }
                fragm = tipc_buf_acquire(fragm_sz + INT_H_SIZE);
                if (fragm == NULL) {
-                       warn("Link unable to fragment message\n");
-                       dsz = -ENOMEM;
-                       goto exit;
+                       buf_discard(buf);
+                       while (buf_chain) {
+                               buf = buf_chain;
+                               buf_chain = buf_chain->next;
+                               buf_discard(buf);
+                       }
+                       return -ENOMEM;
                }
                msg_set_size(&fragm_hdr, fragm_sz + INT_H_SIZE);
+               fragm_no++;
+               msg_set_fragm_no(&fragm_hdr, fragm_no);
                skb_copy_to_linear_data(fragm, &fragm_hdr, INT_H_SIZE);
                skb_copy_to_linear_data_offset(fragm, INT_H_SIZE, crs,
                                               fragm_sz);
-               /*  Send queued messages first, if any: */
+               buf_chain_tail->next = fragm;
+               buf_chain_tail = fragm;
 
-               l_ptr->stats.sent_fragments++;
-               tipc_link_send_buf(l_ptr, fragm);
-               if (!tipc_link_is_up(l_ptr))
-                       return dsz;
-               msg_set_fragm_no(&fragm_hdr, ++fragm_no);
                rest -= fragm_sz;
                crs += fragm_sz;
                msg_set_type(&fragm_hdr, FRAGMENT);
        }
-exit:
        buf_discard(buf);
+
+       /* Append chain of fragments to send queue & send them */
+
+       l_ptr->long_msg_seq_no++;
+       link_add_chain_to_outqueue(l_ptr, buf_chain, l_ptr->long_msg_seq_no);
+       l_ptr->stats.sent_fragments += fragm_no;
+       l_ptr->stats.sent_fragmented++;
+       tipc_link_push_queue(l_ptr);
+
        return dsz;
 }