|
@@ -251,20 +251,23 @@ bool tipc_msg_validate(struct sk_buff **_skb)
|
|
* @pktmax: Max packet size that can be used
|
|
* @pktmax: Max packet size that can be used
|
|
* @list: Buffer or chain of buffers to be returned to caller
|
|
* @list: Buffer or chain of buffers to be returned to caller
|
|
*
|
|
*
|
|
|
|
+ * Note that the recursive call we are making here is safe, since it can
|
|
|
|
+ * logically go only one further level down.
|
|
|
|
+ *
|
|
* Returns message data size or errno: -ENOMEM, -EFAULT
|
|
* Returns message data size or errno: -ENOMEM, -EFAULT
|
|
*/
|
|
*/
|
|
-int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m,
|
|
|
|
- int offset, int dsz, int pktmax, struct sk_buff_head *list)
|
|
|
|
|
|
+int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,
|
|
|
|
+ int dsz, int pktmax, struct sk_buff_head *list)
|
|
{
|
|
{
|
|
int mhsz = msg_hdr_sz(mhdr);
|
|
int mhsz = msg_hdr_sz(mhdr);
|
|
|
|
+ struct tipc_msg pkthdr;
|
|
int msz = mhsz + dsz;
|
|
int msz = mhsz + dsz;
|
|
- int pktno = 1;
|
|
|
|
- int pktsz;
|
|
|
|
int pktrem = pktmax;
|
|
int pktrem = pktmax;
|
|
- int drem = dsz;
|
|
|
|
- struct tipc_msg pkthdr;
|
|
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
|
|
+ int drem = dsz;
|
|
|
|
+ int pktno = 1;
|
|
char *pktpos;
|
|
char *pktpos;
|
|
|
|
+ int pktsz;
|
|
int rc;
|
|
int rc;
|
|
|
|
|
|
msg_set_size(mhdr, msz);
|
|
msg_set_size(mhdr, msz);
|
|
@@ -272,8 +275,18 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m,
|
|
/* No fragmentation needed? */
|
|
/* No fragmentation needed? */
|
|
if (likely(msz <= pktmax)) {
|
|
if (likely(msz <= pktmax)) {
|
|
skb = tipc_buf_acquire(msz, GFP_KERNEL);
|
|
skb = tipc_buf_acquire(msz, GFP_KERNEL);
|
|
- if (unlikely(!skb))
|
|
|
|
|
|
+
|
|
|
|
+ /* Fall back to smaller MTU if node local message */
|
|
|
|
+ if (unlikely(!skb)) {
|
|
|
|
+ if (pktmax != MAX_MSG_SIZE)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ rc = tipc_msg_build(mhdr, m, offset, dsz, FB_MTU, list);
|
|
|
|
+ if (rc != dsz)
|
|
|
|
+ return rc;
|
|
|
|
+ if (tipc_msg_assemble(list))
|
|
|
|
+ return dsz;
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
+ }
|
|
skb_orphan(skb);
|
|
skb_orphan(skb);
|
|
__skb_queue_tail(list, skb);
|
|
__skb_queue_tail(list, skb);
|
|
skb_copy_to_linear_data(skb, mhdr, mhsz);
|
|
skb_copy_to_linear_data(skb, mhdr, mhsz);
|
|
@@ -589,6 +602,30 @@ bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err)
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* tipc_msg_assemble() - assemble chain of fragments into one message
|
|
|
|
+ */
|
|
|
|
+bool tipc_msg_assemble(struct sk_buff_head *list)
|
|
|
|
+{
|
|
|
|
+ struct sk_buff *skb, *tmp = NULL;
|
|
|
|
+
|
|
|
|
+ if (skb_queue_len(list) == 1)
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ while ((skb = __skb_dequeue(list))) {
|
|
|
|
+ skb->next = NULL;
|
|
|
|
+ if (tipc_buf_append(&tmp, &skb)) {
|
|
|
|
+ __skb_queue_tail(list, skb);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ if (!tmp)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ __skb_queue_purge(list);
|
|
|
|
+ __skb_queue_head_init(list);
|
|
|
|
+ pr_warn("Failed do assemble buffer\n");
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
/* tipc_msg_reassemble() - clone a buffer chain of fragments and
|
|
/* tipc_msg_reassemble() - clone a buffer chain of fragments and
|
|
* reassemble the clones into one message
|
|
* reassemble the clones into one message
|
|
*/
|
|
*/
|