diff options
Diffstat (limited to 'net/tipc/msg.c')
| -rw-r--r-- | net/tipc/msg.c | 55 | 
1 files changed, 54 insertions, 1 deletions
diff --git a/net/tipc/msg.c b/net/tipc/msg.c index e525f8ce1dee..8be6e94a1ca9 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -1,7 +1,7 @@  /*   * net/tipc/msg.c: TIPC message header routines   * - * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2000-2006, 2014, Ericsson AB   * Copyright (c) 2005, 2010-2011, Wind River Systems   * All rights reserved.   * @@ -99,3 +99,56 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,  	}  	return dsz;  } + +/* tipc_buf_append(): Append a buffer to the fragment list of another buffer + * Let first buffer become head buffer + * Returns 1 and sets *buf to headbuf if chain is complete, otherwise 0 + * Leaves headbuf pointer at NULL if failure + */ +int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf) +{ +	struct sk_buff *head = *headbuf; +	struct sk_buff *frag = *buf; +	struct sk_buff *tail; +	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 = *headbuf = frag; +		skb_frag_list_init(head); +		return 0; +	} +	if (!head) +		goto out_free; +	tail = TIPC_SKB_CB(head)->tail; +	if (skb_try_coalesce(head, frag, &headstolen, &delta)) { +		kfree_skb_partial(frag, headstolen); +	} else { +		if (!skb_has_frag_list(head)) +			skb_shinfo(head)->frag_list = frag; +		else +			tail->next = frag; +		head->truesize += frag->truesize; +		head->data_len += frag->len; +		head->len += frag->len; +		TIPC_SKB_CB(head)->tail = frag; +	} +	if (fragid == LAST_FRAGMENT) { +		*buf = head; +		TIPC_SKB_CB(head)->tail = NULL; +		*headbuf = NULL; +		return 1; +	} +	*buf = NULL; +	return 0; +out_free: +	pr_warn_ratelimited("Unable to build fragment list\n"); +	kfree_skb(*buf); +	return 0; +}  |