Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / net / core / datagram.c
index 52f577a..95c2e08 100644 (file)
@@ -9,7 +9,7 @@
  *     identical recvmsg() code. So we share it here. The poll was
  *     shared before but buried in udp.c so I moved it.
  *
- *     Authors:        Alan Cox <alan@redhat.com>. (datagram_poll() from old
+ *     Authors:        Alan Cox <alan@lxorguk.ukuu.org.uk>. (datagram_poll() from old
  *                                                  udp.c code)
  *
  *     Fixes:
@@ -55,6 +55,7 @@
 #include <net/checksum.h>
 #include <net/sock.h>
 #include <net/tcp_states.h>
+#include <trace/events/skb.h>
 
 /*
  *     Is a socket 'connection oriented' ?
@@ -64,13 +65,25 @@ static inline int connection_based(struct sock *sk)
        return sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM;
 }
 
+static int receiver_wake_function(wait_queue_t *wait, unsigned mode, int sync,
+                                 void *key)
+{
+       unsigned long bits = (unsigned long)key;
+
+       /*
+        * Avoid a wakeup if event not interesting for us
+        */
+       if (bits && !(bits & (POLLIN | POLLERR)))
+               return 0;
+       return autoremove_wake_function(wait, mode, sync, key);
+}
 /*
  * Wait for a packet..
  */
 static int wait_for_packet(struct sock *sk, int *err, long *timeo_p)
 {
        int error;
-       DEFINE_WAIT(wait);
+       DEFINE_WAIT_FUNC(wait, receiver_wake_function);
 
        prepare_to_wait_exclusive(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
 
@@ -208,9 +221,18 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
 
 void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
 {
-       kfree_skb(skb);
-       sk_mem_reclaim(sk);
+       consume_skb(skb);
+       sk_mem_reclaim_partial(sk);
+}
+EXPORT_SYMBOL(skb_free_datagram);
+
+void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb)
+{
+       lock_sock(sk);
+       skb_free_datagram(sk, skb);
+       release_sock(sk);
 }
+EXPORT_SYMBOL(skb_free_datagram_locked);
 
 /**
  *     skb_kill_datagram - Free a datagram skbuff forcibly
@@ -249,7 +271,9 @@ int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
        }
 
        kfree_skb(skb);
-       sk_mem_reclaim(sk);
+       atomic_inc(&sk->sk_drops);
+       sk_mem_reclaim_partial(sk);
+
        return err;
 }
 
@@ -269,6 +293,9 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
 {
        int start = skb_headlen(skb);
        int i, copy = start - offset;
+       struct sk_buff *frag_iter;
+
+       trace_skb_copy_datagram_iovec(skb, len);
 
        /* Copy header. */
        if (copy > 0) {
@@ -309,28 +336,24 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
                start = end;
        }
 
-       if (skb_shinfo(skb)->frag_list) {
-               struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-               for (; list; list = list->next) {
-                       int end;
-
-                       WARN_ON(start > offset + len);
-
-                       end = start + list->len;
-                       if ((copy = end - offset) > 0) {
-                               if (copy > len)
-                                       copy = len;
-                               if (skb_copy_datagram_iovec(list,
-                                                           offset - start,
-                                                           to, copy))
-                                       goto fault;
-                               if ((len -= copy) == 0)
-                                       return 0;
-                               offset += copy;
-                       }
-                       start = end;
+       skb_walk_frags(skb, frag_iter) {
+               int end;
+
+               WARN_ON(start > offset + len);
+
+               end = start + frag_iter->len;
+               if ((copy = end - offset) > 0) {
+                       if (copy > len)
+                               copy = len;
+                       if (skb_copy_datagram_iovec(frag_iter,
+                                                   offset - start,
+                                                   to, copy))
+                               goto fault;
+                       if ((len -= copy) == 0)
+                               return 0;
+                       offset += copy;
                }
+               start = end;
        }
        if (!len)
                return 0;
@@ -340,30 +363,124 @@ fault:
 }
 
 /**
+ *     skb_copy_datagram_const_iovec - Copy a datagram to an iovec.
+ *     @skb: buffer to copy
+ *     @offset: offset in the buffer to start copying from
+ *     @to: io vector to copy to
+ *     @to_offset: offset in the io vector to start copying to
+ *     @len: amount of data to copy from buffer to iovec
+ *
+ *     Returns 0 or -EFAULT.
+ *     Note: the iovec is not modified during the copy.
+ */
+int skb_copy_datagram_const_iovec(const struct sk_buff *skb, int offset,
+                                 const struct iovec *to, int to_offset,
+                                 int len)
+{
+       int start = skb_headlen(skb);
+       int i, copy = start - offset;
+       struct sk_buff *frag_iter;
+
+       /* Copy header. */
+       if (copy > 0) {
+               if (copy > len)
+                       copy = len;
+               if (memcpy_toiovecend(to, skb->data + offset, to_offset, copy))
+                       goto fault;
+               if ((len -= copy) == 0)
+                       return 0;
+               offset += copy;
+               to_offset += copy;
+       }
+
+       /* Copy paged appendix. Hmm... why does this look so complicated? */
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               int end;
+
+               WARN_ON(start > offset + len);
+
+               end = start + skb_shinfo(skb)->frags[i].size;
+               if ((copy = end - offset) > 0) {
+                       int err;
+                       u8  *vaddr;
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+                       struct page *page = frag->page;
+
+                       if (copy > len)
+                               copy = len;
+                       vaddr = kmap(page);
+                       err = memcpy_toiovecend(to, vaddr + frag->page_offset +
+                                               offset - start, to_offset, copy);
+                       kunmap(page);
+                       if (err)
+                               goto fault;
+                       if (!(len -= copy))
+                               return 0;
+                       offset += copy;
+                       to_offset += copy;
+               }
+               start = end;
+       }
+
+       skb_walk_frags(skb, frag_iter) {
+               int end;
+
+               WARN_ON(start > offset + len);
+
+               end = start + frag_iter->len;
+               if ((copy = end - offset) > 0) {
+                       if (copy > len)
+                               copy = len;
+                       if (skb_copy_datagram_const_iovec(frag_iter,
+                                                         offset - start,
+                                                         to, to_offset,
+                                                         copy))
+                               goto fault;
+                       if ((len -= copy) == 0)
+                               return 0;
+                       offset += copy;
+                       to_offset += copy;
+               }
+               start = end;
+       }
+       if (!len)
+               return 0;
+
+fault:
+       return -EFAULT;
+}
+EXPORT_SYMBOL(skb_copy_datagram_const_iovec);
+
+/**
  *     skb_copy_datagram_from_iovec - Copy a datagram from an iovec.
  *     @skb: buffer to copy
  *     @offset: offset in the buffer to start copying to
  *     @from: io vector to copy to
+ *     @from_offset: offset in the io vector to start copying from
  *     @len: amount of data to copy to buffer from iovec
  *
  *     Returns 0 or -EFAULT.
- *     Note: the iovec is modified during the copy.
+ *     Note: the iovec is not modified during the copy.
  */
 int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
-                                struct iovec *from, int len)
+                                const struct iovec *from, int from_offset,
+                                int len)
 {
        int start = skb_headlen(skb);
        int i, copy = start - offset;
+       struct sk_buff *frag_iter;
 
        /* Copy header. */
        if (copy > 0) {
                if (copy > len)
                        copy = len;
-               if (memcpy_fromiovec(skb->data + offset, from, copy))
+               if (memcpy_fromiovecend(skb->data + offset, from, from_offset,
+                                       copy))
                        goto fault;
                if ((len -= copy) == 0)
                        return 0;
                offset += copy;
+               from_offset += copy;
        }
 
        /* Copy paged appendix. Hmm... why does this look so complicated? */
@@ -382,8 +499,9 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
                        if (copy > len)
                                copy = len;
                        vaddr = kmap(page);
-                       err = memcpy_fromiovec(vaddr + frag->page_offset +
-                                              offset - start, from, copy);
+                       err = memcpy_fromiovecend(vaddr + frag->page_offset +
+                                                 offset - start,
+                                                 from, from_offset, copy);
                        kunmap(page);
                        if (err)
                                goto fault;
@@ -391,32 +509,32 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
                        if (!(len -= copy))
                                return 0;
                        offset += copy;
+                       from_offset += copy;
                }
                start = end;
        }
 
-       if (skb_shinfo(skb)->frag_list) {
-               struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-               for (; list; list = list->next) {
-                       int end;
-
-                       WARN_ON(start > offset + len);
-
-                       end = start + list->len;
-                       if ((copy = end - offset) > 0) {
-                               if (copy > len)
-                                       copy = len;
-                               if (skb_copy_datagram_from_iovec(list,
-                                                                offset - start,
-                                                                from, copy))
-                                       goto fault;
-                               if ((len -= copy) == 0)
-                                       return 0;
-                               offset += copy;
-                       }
-                       start = end;
+       skb_walk_frags(skb, frag_iter) {
+               int end;
+
+               WARN_ON(start > offset + len);
+
+               end = start + frag_iter->len;
+               if ((copy = end - offset) > 0) {
+                       if (copy > len)
+                               copy = len;
+                       if (skb_copy_datagram_from_iovec(frag_iter,
+                                                        offset - start,
+                                                        from,
+                                                        from_offset,
+                                                        copy))
+                               goto fault;
+                       if ((len -= copy) == 0)
+                               return 0;
+                       offset += copy;
+                       from_offset += copy;
                }
+               start = end;
        }
        if (!len)
                return 0;
@@ -431,8 +549,9 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
                                      __wsum *csump)
 {
        int start = skb_headlen(skb);
-       int pos = 0;
        int i, copy = start - offset;
+       struct sk_buff *frag_iter;
+       int pos = 0;
 
        /* Copy header. */
        if (copy > 0) {
@@ -483,33 +602,29 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
                start = end;
        }
 
-       if (skb_shinfo(skb)->frag_list) {
-               struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-               for (; list; list=list->next) {
-                       int end;
-
-                       WARN_ON(start > offset + len);
-
-                       end = start + list->len;
-                       if ((copy = end - offset) > 0) {
-                               __wsum csum2 = 0;
-                               if (copy > len)
-                                       copy = len;
-                               if (skb_copy_and_csum_datagram(list,
-                                                              offset - start,
-                                                              to, copy,
-                                                              &csum2))
-                                       goto fault;
-                               *csump = csum_block_add(*csump, csum2, pos);
-                               if ((len -= copy) == 0)
-                                       return 0;
-                               offset += copy;
-                               to += copy;
-                               pos += copy;
-                       }
-                       start = end;
+       skb_walk_frags(skb, frag_iter) {
+               int end;
+
+               WARN_ON(start > offset + len);
+
+               end = start + frag_iter->len;
+               if ((copy = end - offset) > 0) {
+                       __wsum csum2 = 0;
+                       if (copy > len)
+                               copy = len;
+                       if (skb_copy_and_csum_datagram(frag_iter,
+                                                      offset - start,
+                                                      to, copy,
+                                                      &csum2))
+                               goto fault;
+                       *csump = csum_block_add(*csump, csum2, pos);
+                       if ((len -= copy) == 0)
+                               return 0;
+                       offset += copy;
+                       to += copy;
+                       pos += copy;
                }
+               start = end;
        }
        if (!len)
                return 0;
@@ -610,7 +725,7 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
        struct sock *sk = sock->sk;
        unsigned int mask;
 
-       poll_wait(file, sk->sk_sleep, wait);
+       sock_poll_wait(file, sk->sk_sleep, wait);
        mask = 0;
 
        /* exceptional events? */
@@ -647,5 +762,4 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
 EXPORT_SYMBOL(datagram_poll);
 EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec);
 EXPORT_SYMBOL(skb_copy_datagram_iovec);
-EXPORT_SYMBOL(skb_free_datagram);
 EXPORT_SYMBOL(skb_recv_datagram);