summaryrefslogtreecommitdiff
path: root/net/tipc/link.c
diff options
context:
space:
mode:
authorErik Hugne <erik.hugne@ericsson.com>2013-11-06 09:28:06 +0100
committerDavid S. Miller <davem@davemloft.net>2013-11-07 18:30:11 -0500
commit40ba3cdf542a469aaa9083fa041656e59b109b90 (patch)
treebd7e7af25968b0646155e67b58ab0b03ce3d484d /net/tipc/link.c
parent528f6f4bf372511ebf6004eed822de8f749b5930 (diff)
downloadlinux-stable-40ba3cdf542a469aaa9083fa041656e59b109b90.tar.gz
linux-stable-40ba3cdf542a469aaa9083fa041656e59b109b90.tar.bz2
linux-stable-40ba3cdf542a469aaa9083fa041656e59b109b90.zip
tipc: message reassembly using fragment chain
When the first fragment of a long data data message is received on a link, a reassembly buffer large enough to hold the data from this and all subsequent fragments of the message is allocated. The payload of each new fragment is copied into this buffer upon arrival. When the last fragment is received, the reassembled message is delivered upwards to the port/socket layer. Not only is this an inefficient approach, but it may also cause bursts of reassembly failures in low memory situations. since we may fail to allocate the necessary large buffer in the first place. Furthermore, after 100 subsequent such failures the link will be reset, something that in reality aggravates the situation. To remedy this problem, this patch introduces a different approach. Instead of allocating a big reassembly buffer, we now append the arriving fragments to a reassembly chain on the link, and deliver the whole chain up to the socket layer once the last fragment has been received. This is safe because the retransmission layer of a TIPC link always delivers packets in strict uninterrupted order, to the reassembly layer as to all other upper layers. Hence there can never be more than one fragment chain pending reassembly at any given time in a link, and we can trust (but still verify) that the fragments will be chained up in the correct order. Signed-off-by: Erik Hugne <erik.hugne@ericsson.com> Reviewed-by: Paul Gortmaker <paul.gortmaker@windriver.com> Signed-off-by: Jon Maloy <jon.maloy@ericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc/link.c')
-rw-r--r--net/tipc/link.c159
1 files changed, 44 insertions, 115 deletions
diff --git a/net/tipc/link.c b/net/tipc/link.c
index ada8cadf5af8..a63646e6c2cf 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -404,15 +404,9 @@ static void link_release_outqueue(struct tipc_link *l_ptr)
*/
void tipc_link_reset_fragments(struct tipc_link *l_ptr)
{
- struct sk_buff *buf = l_ptr->defragm_buf;
- struct sk_buff *next;
-
- while (buf) {
- next = buf->next;
- kfree_skb(buf);
- buf = next;
- }
- l_ptr->defragm_buf = NULL;
+ kfree_skb(l_ptr->reasm_head);
+ l_ptr->reasm_head = NULL;
+ l_ptr->reasm_tail = NULL;
}
/**
@@ -1649,13 +1643,15 @@ deliver:
continue;
case MSG_FRAGMENTER:
l_ptr->stats.recv_fragments++;
- ret = tipc_link_recv_fragment(&l_ptr->defragm_buf,
- &buf, &msg);
- if (ret == 1) {
+ ret = tipc_link_recv_fragment(&l_ptr->reasm_head,
+ &l_ptr->reasm_tail,
+ &buf);
+ if (ret == LINK_REASM_COMPLETE) {
l_ptr->stats.recv_fragmented++;
+ msg = buf_msg(buf);
goto deliver;
}
- if (ret == -1)
+ if (ret == LINK_REASM_ERROR)
l_ptr->next_in_no--;
tipc_node_unlock(n_ptr);
continue;
@@ -2343,114 +2339,47 @@ static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
}
/*
- * A pending message being re-assembled must store certain values
- * to handle subsequent fragments correctly. The following functions
- * help storing these values in unused, available fields in the
- * pending message. This makes dynamic memory allocation unnecessary.
- */
-static void set_long_msg_seqno(struct sk_buff *buf, u32 seqno)
-{
- msg_set_seqno(buf_msg(buf), seqno);
-}
-
-static u32 get_fragm_size(struct sk_buff *buf)
-{
- return msg_ack(buf_msg(buf));
-}
-
-static void set_fragm_size(struct sk_buff *buf, u32 sz)
-{
- msg_set_ack(buf_msg(buf), sz);
-}
-
-static u32 get_expected_frags(struct sk_buff *buf)
-{
- return msg_bcast_ack(buf_msg(buf));
-}
-
-static void set_expected_frags(struct sk_buff *buf, u32 exp)
-{
- msg_set_bcast_ack(buf_msg(buf), exp);
-}
-
-/*
* tipc_link_recv_fragment(): Called with node lock on. Returns
* the reassembled buffer if message is complete.
*/
-int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
- struct tipc_msg **m)
-{
- struct sk_buff *prev = NULL;
- struct sk_buff *fbuf = *fb;
- struct tipc_msg *fragm = buf_msg(fbuf);
- struct sk_buff *pbuf = *pending;
- u32 long_msg_seq_no = msg_long_msgno(fragm);
-
- *fb = NULL;
-
- /* Is there an incomplete message waiting for this fragment? */
- while (pbuf && ((buf_seqno(pbuf) != long_msg_seq_no) ||
- (msg_orignode(fragm) != msg_orignode(buf_msg(pbuf))))) {
- prev = pbuf;
- pbuf = pbuf->next;
- }
-
- if (!pbuf && (msg_type(fragm) == FIRST_FRAGMENT)) {
- struct tipc_msg *imsg = (struct tipc_msg *)msg_data(fragm);
- u32 msg_sz = msg_size(imsg);
- u32 fragm_sz = msg_data_sz(fragm);
- u32 exp_fragm_cnt;
- u32 max = TIPC_MAX_USER_MSG_SIZE + NAMED_H_SIZE;
-
- if (msg_type(imsg) == TIPC_MCAST_MSG)
- max = TIPC_MAX_USER_MSG_SIZE + MCAST_H_SIZE;
- if (fragm_sz == 0 || msg_size(imsg) > max) {
- kfree_skb(fbuf);
- return 0;
- }
- exp_fragm_cnt = msg_sz / fragm_sz + !!(msg_sz % fragm_sz);
- pbuf = tipc_buf_acquire(msg_size(imsg));
- if (pbuf != NULL) {
- pbuf->next = *pending;
- *pending = pbuf;
- skb_copy_to_linear_data(pbuf, imsg,
- msg_data_sz(fragm));
- /* Prepare buffer for subsequent fragments. */
- set_long_msg_seqno(pbuf, long_msg_seq_no);
- set_fragm_size(pbuf, fragm_sz);
- set_expected_frags(pbuf, exp_fragm_cnt - 1);
- } else {
- pr_debug("Link unable to reassemble fragmented message\n");
- kfree_skb(fbuf);
- return -1;
- }
- kfree_skb(fbuf);
- return 0;
- } else if (pbuf && (msg_type(fragm) != FIRST_FRAGMENT)) {
- u32 dsz = msg_data_sz(fragm);
- u32 fsz = get_fragm_size(pbuf);
- u32 crs = ((msg_fragm_no(fragm) - 1) * fsz);
- u32 exp_frags = get_expected_frags(pbuf) - 1;
- skb_copy_to_linear_data_offset(pbuf, crs,
- msg_data(fragm), dsz);
- kfree_skb(fbuf);
-
- /* Is message complete? */
- if (exp_frags == 0) {
- if (prev)
- prev->next = pbuf->next;
- else
- *pending = pbuf->next;
- msg_reset_reroute_cnt(buf_msg(pbuf));
- *fb = pbuf;
- *m = buf_msg(pbuf);
- return 1;
- }
- set_expected_frags(pbuf, exp_frags);
+int tipc_link_recv_fragment(struct sk_buff **head, struct sk_buff **tail,
+ struct sk_buff **fbuf)
+{
+ struct sk_buff *frag = *fbuf;
+ struct tipc_msg *msg = buf_msg(frag);
+ u32 fragid = msg_type(msg);
+ bool headstolen;
+ int delta;
+
+ skb_pull(frag, msg_hdr_sz(msg));
+ if (fragid == FIRST_FRAGMENT) {
+ if (*head || skb_unclone(frag, GFP_ATOMIC))
+ goto out_free;
+ *head = frag;
+ skb_frag_list_init(*head);
return 0;
+ } else if (skb_try_coalesce(*head, frag, &headstolen, &delta)) {
+ kfree_skb_partial(frag, headstolen);
+ } else {
+ if (!*head)
+ goto out_free;
+ if (!skb_has_frag_list(*head))
+ skb_shinfo(*head)->frag_list = frag;
+ else
+ (*tail)->next = frag;
+ *tail = frag;
+ (*head)->truesize += frag->truesize;
+ }
+ if (fragid == LAST_FRAGMENT) {
+ *fbuf = *head;
+ *tail = *head = NULL;
+ return LINK_REASM_COMPLETE;
}
- kfree_skb(fbuf);
return 0;
+out_free:
+ pr_warn_ratelimited("Link unable to reassemble fragmented message\n");
+ kfree_skb(*fbuf);
+ return LINK_REASM_ERROR;
}
static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance)