diff options
Diffstat (limited to 'net/tipc/msg.c')
| -rw-r--r-- | net/tipc/msg.c | 83 | 
1 files changed, 68 insertions, 15 deletions
diff --git a/net/tipc/msg.c b/net/tipc/msg.c index ced60e2fc4f..0a37a472c29 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.   * @@ -73,13 +73,13 @@ void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize,   * Returns message data size or errno   */  int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, -		   u32 num_sect, unsigned int total_len, int max_size, -		   struct sk_buff **buf) +		   unsigned int len, int max_size, struct sk_buff **buf)  { -	int dsz, sz, hsz, pos, res, cnt; +	int dsz, sz, hsz; +	unsigned char *to; -	dsz = total_len; -	pos = hsz = msg_hdr_sz(hdr); +	dsz = len; +	hsz = msg_hdr_sz(hdr);  	sz = hsz + dsz;  	msg_set_size(hdr, sz);  	if (unlikely(sz > max_size)) { @@ -91,16 +91,69 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,  	if (!(*buf))  		return -ENOMEM;  	skb_copy_to_linear_data(*buf, hdr, hsz); -	for (res = 1, cnt = 0; res && (cnt < num_sect); cnt++) { -		skb_copy_to_linear_data_offset(*buf, pos, -					       msg_sect[cnt].iov_base, -					       msg_sect[cnt].iov_len); -		pos += msg_sect[cnt].iov_len; +	to = (*buf)->data + hsz; +	if (len && memcpy_fromiovecend(to, msg_sect, 0, dsz)) { +		kfree_skb(*buf); +		*buf = NULL; +		return -EFAULT;  	} -	if (likely(res)) -		return dsz; +	return dsz; +} -	kfree_skb(*buf); +/* tipc_buf_append(): Append a buffer to the fragment list of another buffer + * @*headbuf: in:  NULL for first frag, otherwise value returned from prev call + *            out: set when successful non-complete reassembly, otherwise NULL + * @*buf:     in:  the buffer to append. Always defined + *            out: head buf after sucessful complete reassembly, otherwise NULL + * Returns 1 when reassembly complete, otherwise 0 + */ +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); +		*buf = NULL; +		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 -EFAULT; +	return 0; +out_free: +	pr_warn_ratelimited("Unable to build fragment list\n"); +	kfree_skb(*buf); +	kfree_skb(*headbuf); +	*buf = *headbuf = NULL; +	return 0;  }  | 
