static LIST_HEAD(ipq_lru_list);
 int ip_frag_nqueues = 0;
 
+static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
+                        struct net_device *dev);
+
 static __inline__ void __ipq_unlink(struct ipq *qp)
 {
        hlist_del(&qp->list);
 }
 
 /* Add new segment to existing queue. */
-static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
+static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
 {
        struct sk_buff *prev, *next;
+       struct net_device *dev;
        int flags, offset;
        int ihl, end;
+       int err = -ENOENT;
 
        if (qp->last_in & COMPLETE)
                goto err;
 
        if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
-           unlikely(ip_frag_too_far(qp)) && unlikely(ip_frag_reinit(qp))) {
+           unlikely(ip_frag_too_far(qp)) &&
+           unlikely(err = ip_frag_reinit(qp))) {
                ipq_kill(qp);
                goto err;
        }
 
        /* Determine the position of this fragment. */
        end = offset + skb->len - ihl;
+       err = -EINVAL;
 
        /* Is this the final fragment? */
        if ((flags & IP_MF) == 0) {
        if (end == offset)
                goto err;
 
+       err = -ENOMEM;
        if (pskb_pull(skb, ihl) == NULL)
                goto err;
-       if (pskb_trim_rcsum(skb, end-offset))
+
+       err = pskb_trim_rcsum(skb, end - offset);
+       if (err)
                goto err;
 
        /* Find out which fragments are in front and at the back of us
 
                if (i > 0) {
                        offset += i;
+                       err = -EINVAL;
                        if (end <= offset)
                                goto err;
+                       err = -ENOMEM;
                        if (!pskb_pull(skb, i))
                                goto err;
                        if (skb->ip_summed != CHECKSUM_UNNECESSARY)
                }
        }
 
+       err = -ENOMEM;
+
        while (next && FRAG_CB(next)->offset < end) {
                int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */
 
        else
                qp->fragments = skb;
 
-       if (skb->dev)
-               qp->iif = skb->dev->ifindex;
-       skb->dev = NULL;
+       dev = skb->dev;
+       if (dev) {
+               qp->iif = dev->ifindex;
+               skb->dev = NULL;
+       }
        qp->stamp = skb->tstamp;
        qp->meat += skb->len;
        atomic_add(skb->truesize, &ip_frag_mem);
        if (offset == 0)
                qp->last_in |= FIRST_IN;
 
+       if (qp->last_in == (FIRST_IN | LAST_IN) && qp->meat == qp->len)
+               return ip_frag_reasm(qp, prev, dev);
+
        write_lock(&ipfrag_lock);
        list_move_tail(&qp->lru_list, &ipq_lru_list);
        write_unlock(&ipfrag_lock);
-
-       return;
+       return -EINPROGRESS;
 
 err:
        kfree_skb(skb);
+       return err;
 }
 
 
 /* Build a new IP datagram from all its fragments. */
 
-static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
+static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
+                        struct net_device *dev)
 {
        struct iphdr *iph;
        struct sk_buff *fp, *head = qp->fragments;
        int len;
        int ihlen;
+       int err;
 
        ipq_kill(qp);
 
+       /* Make the one we just received the head. */
+       if (prev) {
+               head = prev->next;
+               fp = skb_clone(head, GFP_ATOMIC);
+
+               if (!fp)
+                       goto out_nomem;
+
+               fp->next = head->next;
+               prev->next = fp;
+
+               skb_morph(head, qp->fragments);
+               head->next = qp->fragments->next;
+
+               kfree_skb(qp->fragments);
+               qp->fragments = head;
+       }
+
        BUG_TRAP(head != NULL);
        BUG_TRAP(FRAG_CB(head)->offset == 0);
 
        ihlen = ip_hdrlen(head);
        len = ihlen + qp->len;
 
+       err = -E2BIG;
        if (len > 65535)
                goto out_oversize;
 
        /* Head of list must not be cloned. */
+       err = -ENOMEM;
        if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
                goto out_nomem;
 
        iph->tot_len = htons(len);
        IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
        qp->fragments = NULL;
-       return head;
+       return 0;
 
 out_nomem:
        LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
                        NIPQUAD(qp->saddr));
 out_fail:
        IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
-       return NULL;
+       return err;
 }
 
 /* Process an incoming IP datagram fragment. */
 struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user)
 {
        struct ipq *qp;
-       struct net_device *dev;
 
        IP_INC_STATS_BH(IPSTATS_MIB_REASMREQDS);
 
        if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
                ip_evictor();
 
-       dev = skb->dev;
-
        /* Lookup (or create) queue header */
        if ((qp = ip_find(ip_hdr(skb), user)) != NULL) {
-               struct sk_buff *ret = NULL;
+               int ret;
 
                spin_lock(&qp->lock);
 
-               ip_frag_queue(qp, skb);
-
-               if (qp->last_in == (FIRST_IN|LAST_IN) &&
-                   qp->meat == qp->len)
-                       ret = ip_frag_reasm(qp, dev);
+               ret = ip_frag_queue(qp, skb);
 
                spin_unlock(&qp->lock);
                ipq_put(qp, NULL);
-               return ret;
+               return ret ? NULL : skb;
        }
 
        IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);