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 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * net/tipc/msg.c: TIPC message header routines | 2 | * net/tipc/msg.c: TIPC message header routines |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2000-2006, Ericsson AB | 4 | * Copyright (c) 2000-2006, 2014, Ericsson AB |
| 5 | * Copyright (c) 2005, 2010-2011, Wind River Systems | 5 | * Copyright (c) 2005, 2010-2011, Wind River Systems |
| 6 | * All rights reserved. | 6 | * All rights reserved. |
| 7 | * | 7 | * |
| @@ -99,3 +99,56 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, | |||
| 99 | } | 99 | } |
| 100 | return dsz; | 100 | return dsz; |
| 101 | } | 101 | } |
| 102 | |||
| 103 | /* tipc_buf_append(): Append a buffer to the fragment list of another buffer | ||
| 104 | * Let first buffer become head buffer | ||
| 105 | * Returns 1 and sets *buf to headbuf if chain is complete, otherwise 0 | ||
| 106 | * Leaves headbuf pointer at NULL if failure | ||
| 107 | */ | ||
| 108 | int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf) | ||
| 109 | { | ||
| 110 | struct sk_buff *head = *headbuf; | ||
| 111 | struct sk_buff *frag = *buf; | ||
| 112 | struct sk_buff *tail; | ||
| 113 | struct tipc_msg *msg = buf_msg(frag); | ||
| 114 | u32 fragid = msg_type(msg); | ||
| 115 | bool headstolen; | ||
| 116 | int delta; | ||
| 117 | |||
| 118 | skb_pull(frag, msg_hdr_sz(msg)); | ||
| 119 | |||
| 120 | if (fragid == FIRST_FRAGMENT) { | ||
| 121 | if (head || skb_unclone(frag, GFP_ATOMIC)) | ||
| 122 | goto out_free; | ||
| 123 | head = *headbuf = frag; | ||
| 124 | skb_frag_list_init(head); | ||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | if (!head) | ||
| 128 | goto out_free; | ||
| 129 | tail = TIPC_SKB_CB(head)->tail; | ||
| 130 | if (skb_try_coalesce(head, frag, &headstolen, &delta)) { | ||
| 131 | kfree_skb_partial(frag, headstolen); | ||
| 132 | } else { | ||
| 133 | if (!skb_has_frag_list(head)) | ||
| 134 | skb_shinfo(head)->frag_list = frag; | ||
| 135 | else | ||
| 136 | tail->next = frag; | ||
| 137 | head->truesize += frag->truesize; | ||
| 138 | head->data_len += frag->len; | ||
| 139 | head->len += frag->len; | ||
| 140 | TIPC_SKB_CB(head)->tail = frag; | ||
| 141 | } | ||
| 142 | if (fragid == LAST_FRAGMENT) { | ||
| 143 | *buf = head; | ||
| 144 | TIPC_SKB_CB(head)->tail = NULL; | ||
| 145 | *headbuf = NULL; | ||
| 146 | return 1; | ||
| 147 | } | ||
| 148 | *buf = NULL; | ||
| 149 | return 0; | ||
| 150 | out_free: | ||
| 151 | pr_warn_ratelimited("Unable to build fragment list\n"); | ||
| 152 | kfree_skb(*buf); | ||
| 153 | return 0; | ||
| 154 | } | ||
