diff options
Diffstat (limited to 'net/tipc/msg.c')
-rw-r--r-- | net/tipc/msg.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/net/tipc/msg.c b/net/tipc/msg.c index e02afc96edd7..4093b4c94d26 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c | |||
@@ -144,6 +144,108 @@ out_free: | |||
144 | return 0; | 144 | return 0; |
145 | } | 145 | } |
146 | 146 | ||
147 | |||
148 | /** | ||
149 | * tipc_msg_build2 - create buffer chain containing specified header and data | ||
150 | * @mhdr: Message header, to be prepended to data | ||
151 | * @iov: User data | ||
152 | * @offset: Posision in iov to start copying from | ||
153 | * @dsz: Total length of user data | ||
154 | * @pktmax: Max packet size that can be used | ||
155 | * @chain: Buffer or chain of buffers to be returned to caller | ||
156 | * Returns message data size or errno: -ENOMEM, -EFAULT | ||
157 | */ | ||
158 | int tipc_msg_build2(struct tipc_msg *mhdr, struct iovec const *iov, | ||
159 | int offset, int dsz, int pktmax , struct sk_buff **chain) | ||
160 | { | ||
161 | int mhsz = msg_hdr_sz(mhdr); | ||
162 | int msz = mhsz + dsz; | ||
163 | int pktno = 1; | ||
164 | int pktsz; | ||
165 | int pktrem = pktmax; | ||
166 | int drem = dsz; | ||
167 | struct tipc_msg pkthdr; | ||
168 | struct sk_buff *buf, *prev; | ||
169 | char *pktpos; | ||
170 | int rc; | ||
171 | |||
172 | msg_set_size(mhdr, msz); | ||
173 | |||
174 | /* No fragmentation needed? */ | ||
175 | if (likely(msz <= pktmax)) { | ||
176 | buf = tipc_buf_acquire(msz); | ||
177 | *chain = buf; | ||
178 | if (unlikely(!buf)) | ||
179 | return -ENOMEM; | ||
180 | skb_copy_to_linear_data(buf, mhdr, mhsz); | ||
181 | pktpos = buf->data + mhsz; | ||
182 | if (!dsz || !memcpy_fromiovecend(pktpos, iov, offset, dsz)) | ||
183 | return dsz; | ||
184 | rc = -EFAULT; | ||
185 | goto error; | ||
186 | } | ||
187 | |||
188 | /* Prepare reusable fragment header */ | ||
189 | tipc_msg_init(&pkthdr, MSG_FRAGMENTER, FIRST_FRAGMENT, | ||
190 | INT_H_SIZE, msg_destnode(mhdr)); | ||
191 | msg_set_size(&pkthdr, pktmax); | ||
192 | msg_set_fragm_no(&pkthdr, pktno); | ||
193 | |||
194 | /* Prepare first fragment */ | ||
195 | *chain = buf = tipc_buf_acquire(pktmax); | ||
196 | if (!buf) | ||
197 | return -ENOMEM; | ||
198 | pktpos = buf->data; | ||
199 | skb_copy_to_linear_data(buf, &pkthdr, INT_H_SIZE); | ||
200 | pktpos += INT_H_SIZE; | ||
201 | pktrem -= INT_H_SIZE; | ||
202 | skb_copy_to_linear_data_offset(buf, INT_H_SIZE, mhdr, mhsz); | ||
203 | pktpos += mhsz; | ||
204 | pktrem -= mhsz; | ||
205 | |||
206 | do { | ||
207 | if (drem < pktrem) | ||
208 | pktrem = drem; | ||
209 | |||
210 | if (memcpy_fromiovecend(pktpos, iov, offset, pktrem)) { | ||
211 | rc = -EFAULT; | ||
212 | goto error; | ||
213 | } | ||
214 | drem -= pktrem; | ||
215 | offset += pktrem; | ||
216 | |||
217 | if (!drem) | ||
218 | break; | ||
219 | |||
220 | /* Prepare new fragment: */ | ||
221 | if (drem < (pktmax - INT_H_SIZE)) | ||
222 | pktsz = drem + INT_H_SIZE; | ||
223 | else | ||
224 | pktsz = pktmax; | ||
225 | prev = buf; | ||
226 | buf = tipc_buf_acquire(pktsz); | ||
227 | if (!buf) { | ||
228 | rc = -ENOMEM; | ||
229 | goto error; | ||
230 | } | ||
231 | prev->next = buf; | ||
232 | msg_set_type(&pkthdr, FRAGMENT); | ||
233 | msg_set_size(&pkthdr, pktsz); | ||
234 | msg_set_fragm_no(&pkthdr, ++pktno); | ||
235 | skb_copy_to_linear_data(buf, &pkthdr, INT_H_SIZE); | ||
236 | pktpos = buf->data + INT_H_SIZE; | ||
237 | pktrem = pktsz - INT_H_SIZE; | ||
238 | |||
239 | } while (1); | ||
240 | |||
241 | msg_set_type(buf_msg(buf), LAST_FRAGMENT); | ||
242 | return dsz; | ||
243 | error: | ||
244 | kfree_skb_list(*chain); | ||
245 | *chain = NULL; | ||
246 | return rc; | ||
247 | } | ||
248 | |||
147 | /** | 249 | /** |
148 | * tipc_msg_bundle(): Append contents of a buffer to tail of an existing one | 250 | * tipc_msg_bundle(): Append contents of a buffer to tail of an existing one |
149 | * @bbuf: the existing buffer ("bundle") | 251 | * @bbuf: the existing buffer ("bundle") |