Merge tag 'arc-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc
[linux-2.6-microblaze.git] / net / tipc / msg.c
index 922d262..0d515d2 100644 (file)
 #include "msg.h"
 #include "addr.h"
 #include "name_table.h"
+#include "crypto.h"
 
 #define MAX_FORWARD_SIZE 1024
+#ifdef CONFIG_TIPC_CRYPTO
+#define BUF_HEADROOM ALIGN(((LL_MAX_HEADER + 48) + EHDR_MAX_SIZE), 16)
+#define BUF_TAILROOM (TIPC_AES_GCM_TAG_SIZE)
+#else
 #define BUF_HEADROOM (LL_MAX_HEADER + 48)
 #define BUF_TAILROOM 16
+#endif
 
 static unsigned int align(unsigned int i)
 {
@@ -61,7 +67,11 @@ static unsigned int align(unsigned int i)
 struct sk_buff *tipc_buf_acquire(u32 size, gfp_t gfp)
 {
        struct sk_buff *skb;
+#ifdef CONFIG_TIPC_CRYPTO
+       unsigned int buf_size = (BUF_HEADROOM + size + BUF_TAILROOM + 3) & ~3u;
+#else
        unsigned int buf_size = (BUF_HEADROOM + size + 3) & ~3u;
+#endif
 
        skb = alloc_skb_fclone(buf_size, gfp);
        if (skb) {
@@ -173,7 +183,7 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
        }
 
        if (fragid == LAST_FRAGMENT) {
-               TIPC_SKB_CB(head)->validated = false;
+               TIPC_SKB_CB(head)->validated = 0;
                if (unlikely(!tipc_msg_validate(&head)))
                        goto err;
                *buf = head;
@@ -190,6 +200,59 @@ err:
        return 0;
 }
 
+/**
+ * tipc_msg_append(): Append data to tail of an existing buffer queue
+ * @hdr: header to be used
+ * @m: the data to be appended
+ * @mss: max allowable size of buffer
+ * @dlen: size of data to be appended
+ * @txq: queue to appand to
+ * Returns the number og 1k blocks appended or errno value
+ */
+int tipc_msg_append(struct tipc_msg *_hdr, struct msghdr *m, int dlen,
+                   int mss, struct sk_buff_head *txq)
+{
+       struct sk_buff *skb, *prev;
+       int accounted, total, curr;
+       int mlen, cpy, rem = dlen;
+       struct tipc_msg *hdr;
+
+       skb = skb_peek_tail(txq);
+       accounted = skb ? msg_blocks(buf_msg(skb)) : 0;
+       total = accounted;
+
+       while (rem) {
+               if (!skb || skb->len >= mss) {
+                       prev = skb;
+                       skb = tipc_buf_acquire(mss, GFP_KERNEL);
+                       if (unlikely(!skb))
+                               return -ENOMEM;
+                       skb_orphan(skb);
+                       skb_trim(skb, MIN_H_SIZE);
+                       hdr = buf_msg(skb);
+                       skb_copy_to_linear_data(skb, _hdr, MIN_H_SIZE);
+                       msg_set_hdr_sz(hdr, MIN_H_SIZE);
+                       msg_set_size(hdr, MIN_H_SIZE);
+                       __skb_queue_tail(txq, skb);
+                       total += 1;
+                       if (prev)
+                               msg_set_ack_required(buf_msg(prev), 0);
+                       msg_set_ack_required(hdr, 1);
+               }
+               hdr = buf_msg(skb);
+               curr = msg_blocks(hdr);
+               mlen = msg_size(hdr);
+               cpy = min_t(int, rem, mss - mlen);
+               if (cpy != copy_from_iter(skb->data + mlen, cpy, &m->msg_iter))
+                       return -EFAULT;
+               msg_set_size(hdr, mlen + cpy);
+               skb_put(skb, cpy);
+               rem -= cpy;
+               total += msg_blocks(hdr) - curr;
+       }
+       return total - accounted;
+}
+
 /* tipc_msg_validate - validate basic format of received message
  *
  * This routine ensures a TIPC message has an acceptable header, and at least
@@ -218,6 +281,7 @@ bool tipc_msg_validate(struct sk_buff **_skb)
 
        if (unlikely(TIPC_SKB_CB(skb)->validated))
                return true;
+
        if (unlikely(!pskb_may_pull(skb, MIN_H_SIZE)))
                return false;
 
@@ -239,7 +303,7 @@ bool tipc_msg_validate(struct sk_buff **_skb)
        if (unlikely(skb->len < msz))
                return false;
 
-       TIPC_SKB_CB(skb)->validated = true;
+       TIPC_SKB_CB(skb)->validated = 1;
        return true;
 }
 
@@ -419,48 +483,98 @@ error:
 }
 
 /**
- * tipc_msg_bundle(): Append contents of a buffer to tail of an existing one
- * @skb: the buffer to append to ("bundle")
- * @msg:  message to be appended
- * @mtu:  max allowable size for the bundle buffer
- * Consumes buffer if successful
- * Returns true if bundling could be performed, otherwise false
+ * tipc_msg_bundle - Append contents of a buffer to tail of an existing one
+ * @bskb: the bundle buffer to append to
+ * @msg: message to be appended
+ * @max: max allowable size for the bundle buffer
+ *
+ * Returns "true" if bundling has been performed, otherwise "false"
  */
-bool tipc_msg_bundle(struct sk_buff *skb, struct tipc_msg *msg, u32 mtu)
+static bool tipc_msg_bundle(struct sk_buff *bskb, struct tipc_msg *msg,
+                           u32 max)
 {
-       struct tipc_msg *bmsg;
-       unsigned int bsz;
-       unsigned int msz = msg_size(msg);
-       u32 start, pad;
-       u32 max = mtu - INT_H_SIZE;
+       struct tipc_msg *bmsg = buf_msg(bskb);
+       u32 msz, bsz, offset, pad;
 
-       if (likely(msg_user(msg) == MSG_FRAGMENTER))
-               return false;
-       if (!skb)
-               return false;
-       bmsg = buf_msg(skb);
+       msz = msg_size(msg);
        bsz = msg_size(bmsg);
-       start = align(bsz);
-       pad = start - bsz;
+       offset = align(bsz);
+       pad = offset - bsz;
 
-       if (unlikely(msg_user(msg) == TUNNEL_PROTOCOL))
+       if (unlikely(skb_tailroom(bskb) < (pad + msz)))
                return false;
-       if (unlikely(msg_user(msg) == BCAST_PROTOCOL))
+       if (unlikely(max < (offset + msz)))
                return false;
-       if (unlikely(msg_user(bmsg) != MSG_BUNDLER))
+
+       skb_put(bskb, pad + msz);
+       skb_copy_to_linear_data_offset(bskb, offset, msg, msz);
+       msg_set_size(bmsg, offset + msz);
+       msg_set_msgcnt(bmsg, msg_msgcnt(bmsg) + 1);
+       return true;
+}
+
+/**
+ * tipc_msg_try_bundle - Try to bundle a new message to the last one
+ * @tskb: the last/target message to which the new one will be appended
+ * @skb: the new message skb pointer
+ * @mss: max message size (header inclusive)
+ * @dnode: destination node for the message
+ * @new_bundle: if this call made a new bundle or not
+ *
+ * Return: "true" if the new message skb is potential for bundling this time or
+ * later, in the case a bundling has been done this time, the skb is consumed
+ * (the skb pointer = NULL).
+ * Otherwise, "false" if the skb cannot be bundled at all.
+ */
+bool tipc_msg_try_bundle(struct sk_buff *tskb, struct sk_buff **skb, u32 mss,
+                        u32 dnode, bool *new_bundle)
+{
+       struct tipc_msg *msg, *inner, *outer;
+       u32 tsz;
+
+       /* First, check if the new buffer is suitable for bundling */
+       msg = buf_msg(*skb);
+       if (msg_user(msg) == MSG_FRAGMENTER)
                return false;
-       if (unlikely(skb_tailroom(skb) < (pad + msz)))
+       if (msg_user(msg) == TUNNEL_PROTOCOL)
                return false;
-       if (unlikely(max < (start + msz)))
+       if (msg_user(msg) == BCAST_PROTOCOL)
                return false;
-       if ((msg_importance(msg) < TIPC_SYSTEM_IMPORTANCE) &&
-           (msg_importance(bmsg) == TIPC_SYSTEM_IMPORTANCE))
+       if (mss <= INT_H_SIZE + msg_size(msg))
                return false;
 
-       skb_put(skb, pad + msz);
-       skb_copy_to_linear_data_offset(skb, start, msg, msz);
-       msg_set_size(bmsg, start + msz);
-       msg_set_msgcnt(bmsg, msg_msgcnt(bmsg) + 1);
+       /* Ok, but the last/target buffer can be empty? */
+       if (unlikely(!tskb))
+               return true;
+
+       /* Is it a bundle already? Try to bundle the new message to it */
+       if (msg_user(buf_msg(tskb)) == MSG_BUNDLER) {
+               *new_bundle = false;
+               goto bundle;
+       }
+
+       /* Make a new bundle of the two messages if possible */
+       tsz = msg_size(buf_msg(tskb));
+       if (unlikely(mss < align(INT_H_SIZE + tsz) + msg_size(msg)))
+               return true;
+       if (unlikely(pskb_expand_head(tskb, INT_H_SIZE, mss - tsz - INT_H_SIZE,
+                                     GFP_ATOMIC)))
+               return true;
+       inner = buf_msg(tskb);
+       skb_push(tskb, INT_H_SIZE);
+       outer = buf_msg(tskb);
+       tipc_msg_init(msg_prevnode(inner), outer, MSG_BUNDLER, 0, INT_H_SIZE,
+                     dnode);
+       msg_set_importance(outer, msg_importance(inner));
+       msg_set_size(outer, INT_H_SIZE + tsz);
+       msg_set_msgcnt(outer, 1);
+       *new_bundle = true;
+
+bundle:
+       if (likely(tipc_msg_bundle(tskb, msg, mss))) {
+               consume_skb(*skb);
+               *skb = NULL;
+       }
        return true;
 }
 
@@ -509,49 +623,6 @@ none:
        return false;
 }
 
-/**
- * tipc_msg_make_bundle(): Create bundle buf and append message to its tail
- * @list: the buffer chain, where head is the buffer to replace/append
- * @skb: buffer to be created, appended to and returned in case of success
- * @msg: message to be appended
- * @mtu: max allowable size for the bundle buffer, inclusive header
- * @dnode: destination node for message. (Not always present in header)
- * Returns true if success, otherwise false
- */
-bool tipc_msg_make_bundle(struct sk_buff **skb,  struct tipc_msg *msg,
-                         u32 mtu, u32 dnode)
-{
-       struct sk_buff *_skb;
-       struct tipc_msg *bmsg;
-       u32 msz = msg_size(msg);
-       u32 max = mtu - INT_H_SIZE;
-
-       if (msg_user(msg) == MSG_FRAGMENTER)
-               return false;
-       if (msg_user(msg) == TUNNEL_PROTOCOL)
-               return false;
-       if (msg_user(msg) == BCAST_PROTOCOL)
-               return false;
-       if (msz > (max / 2))
-               return false;
-
-       _skb = tipc_buf_acquire(max, GFP_ATOMIC);
-       if (!_skb)
-               return false;
-
-       skb_trim(_skb, INT_H_SIZE);
-       bmsg = buf_msg(_skb);
-       tipc_msg_init(msg_prevnode(msg), bmsg, MSG_BUNDLER, 0,
-                     INT_H_SIZE, dnode);
-       msg_set_importance(bmsg, msg_importance(msg));
-       msg_set_seqno(bmsg, msg_seqno(msg));
-       msg_set_ack(bmsg, msg_ack(msg));
-       msg_set_bcast_ack(bmsg, msg_bcast_ack(msg));
-       tipc_msg_bundle(_skb, msg, mtu);
-       *skb = _skb;
-       return true;
-}
-
 /**
  * tipc_msg_reverse(): swap source and destination addresses and add error code
  * @own_node: originating node id for reversed message