Revert "isdn: isdn_ppp: Use SKB list facilities instead of home-grown implementation."
authorDavid S. Miller <davem@davemloft.net>
Mon, 16 Nov 2009 06:23:47 +0000 (22:23 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 16 Nov 2009 06:23:47 +0000 (22:23 -0800)
This reverts commit 38783e671399b5405f1fd177d602c400a9577ae6.

It causes kernel bugzilla #14594

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/isdn/i4l/isdn_ppp.c
include/linux/isdn_ppp.h

index 2d14b64..642d5aa 100644 (file)
@@ -1535,10 +1535,8 @@ static int isdn_ppp_mp_bundle_array_init(void)
        int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
        if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL )
                return -ENOMEM;
-       for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+       for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
                spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
-               skb_queue_head_init(&isdn_ppp_bundle_arr[i].frags);
-       }
        return 0;
 }
 
@@ -1571,7 +1569,7 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
                if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
                        return -ENOMEM;
                lp->next = lp->last = lp;       /* nobody else in a queue */
-               skb_queue_head_init(&lp->netdev->pb->frags);
+               lp->netdev->pb->frags = NULL;
                lp->netdev->pb->frames = 0;
                lp->netdev->pb->seq = UINT_MAX;
        }
@@ -1583,29 +1581,28 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
 
 static u32 isdn_ppp_mp_get_seq( int short_seq, 
                                        struct sk_buff * skb, u32 last_seq );
-static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from,
-                               struct sk_buff *to);
-static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp,
-                                  struct sk_buff *from, struct sk_buff *to,
-                                  u32 lastseq);
-static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb);
+static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+                       struct sk_buff * from, struct sk_buff * to );
+static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
+                               struct sk_buff * from, struct sk_buff * to );
+static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );
 static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );
 
 static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
-                               struct sk_buff *skb)
+                                                       struct sk_buff *skb)
 {
-       struct sk_buff *newfrag, *frag, *start, *nextf;
-       u32 newseq, minseq, thisseq;
-       isdn_mppp_stats *stats;
        struct ippp_struct *is;
+       isdn_net_local * lpq;
+       ippp_bundle * mp;
+       isdn_mppp_stats * stats;
+       struct sk_buff * newfrag, * frag, * start, *nextf;
+       u32 newseq, minseq, thisseq;
        unsigned long flags;
-       isdn_net_local *lpq;
-       ippp_bundle *mp;
        int slot;
 
        spin_lock_irqsave(&net_dev->pb->lock, flags);
-       mp = net_dev->pb;
-       stats = &mp->stats;
+       mp = net_dev->pb;
+        stats = &mp->stats;
        slot = lp->ppp_slot;
        if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
                printk(KERN_ERR "%s: lp->ppp_slot(%d)\n",
@@ -1616,19 +1613,20 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
                return;
        }
        is = ippp_table[slot];
-       if (++mp->frames > stats->max_queue_len)
+       if( ++mp->frames > stats->max_queue_len )
                stats->max_queue_len = mp->frames;
-
+       
        if (is->debug & 0x8)
                isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
 
-       newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ,
-                                    skb, is->last_link_seqno);
+       newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, 
+                                               skb, is->last_link_seqno);
+
 
        /* if this packet seq # is less than last already processed one,
         * toss it right away, but check for sequence start case first 
         */
-       if (mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT)) {
+       if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) {
                mp->seq = newseq;       /* the first packet: required for
                                         * rfc1990 non-compliant clients --
                                         * prevents constant packet toss */
@@ -1638,7 +1636,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
                spin_unlock_irqrestore(&mp->lock, flags);
                return;
        }
-
+       
        /* find the minimum received sequence number over all links */
        is->last_link_seqno = minseq = newseq;
        for (lpq = net_dev->queue;;) {
@@ -1659,31 +1657,22 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
                                         * packets */
        newfrag = skb;
 
-       /* Insert new fragment into the proper sequence slot.  */
-       skb_queue_walk(&mp->frags, frag) {
-               if (MP_SEQ(frag) == newseq) {
-                       isdn_ppp_mp_free_skb(mp, newfrag);
-                       newfrag = NULL;
-                       break;
-               }
-               if (MP_LT(newseq, MP_SEQ(frag))) {
-                       __skb_queue_before(&mp->frags, frag, newfrag);
-                       newfrag = NULL;
-                       break;
-               }
-       }
-       if (newfrag)
-               __skb_queue_tail(&mp->frags, newfrag);
+       /* if this new fragment is before the first one, then enqueue it now. */
+       if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) {
+               newfrag->next = frag;
+               mp->frags = frag = newfrag;
+               newfrag = NULL;
+       }
 
-       frag = skb_peek(&mp->frags);
-       start = ((MP_FLAGS(frag) & MP_BEGIN_FRAG) &&
-                (MP_SEQ(frag) == mp->seq)) ? frag : NULL;
-       if (!start)
-               goto check_overflow;
+       start = MP_FLAGS(frag) & MP_BEGIN_FRAG &&
+                               MP_SEQ(frag) == mp->seq ? frag : NULL;
 
-       /* main fragment traversing loop
+       /* 
+        * main fragment traversing loop
         *
         * try to accomplish several tasks:
+        * - insert new fragment into the proper sequence slot (once that's done
+        *   newfrag will be set to NULL)
         * - reassemble any complete fragment sequence (non-null 'start'
         *   indicates there is a continguous sequence present)
         * - discard any incomplete sequences that are below minseq -- due
@@ -1692,46 +1681,71 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
         *   come to complete such sequence and it should be discarded
         *
         * loop completes when we accomplished the following tasks:
+        * - new fragment is inserted in the proper sequence ('newfrag' is 
+        *   set to NULL)
         * - we hit a gap in the sequence, so no reassembly/processing is 
         *   possible ('start' would be set to NULL)
         *
         * algorithm for this code is derived from code in the book
         * 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
         */
-       skb_queue_walk_safe(&mp->frags, frag, nextf) {
-               thisseq = MP_SEQ(frag);
-
-               /* check for misplaced start */
-               if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {
-                       printk(KERN_WARNING"isdn_mppp(seq %d): new "
-                              "BEGIN flag with no prior END", thisseq);
-                       stats->seqerrs++;
-                       stats->frame_drops++;
-                       isdn_ppp_mp_discard(mp, start, frag);
-                       start = frag;
-               } else if (MP_LE(thisseq, minseq)) {            
-                       if (MP_FLAGS(frag) & MP_BEGIN_FRAG)
+       while (start != NULL || newfrag != NULL) {
+
+               thisseq = MP_SEQ(frag);
+               nextf = frag->next;
+
+               /* drop any duplicate fragments */
+               if (newfrag != NULL && thisseq == newseq) {
+                       isdn_ppp_mp_free_skb(mp, newfrag);
+                       newfrag = NULL;
+               }
+
+               /* insert new fragment before next element if possible. */
+               if (newfrag != NULL && (nextf == NULL || 
+                                               MP_LT(newseq, MP_SEQ(nextf)))) {
+                       newfrag->next = nextf;
+                       frag->next = nextf = newfrag;
+                       newfrag = NULL;
+               }
+
+               if (start != NULL) {
+                       /* check for misplaced start */
+                       if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {
+                               printk(KERN_WARNING"isdn_mppp(seq %d): new "
+                                     "BEGIN flag with no prior END", thisseq);
+                               stats->seqerrs++;
+                               stats->frame_drops++;
+                               start = isdn_ppp_mp_discard(mp, start,frag);
+                               nextf = frag->next;
+                       }
+               } else if (MP_LE(thisseq, minseq)) {            
+                       if (MP_FLAGS(frag) & MP_BEGIN_FRAG)
                                start = frag;
-                       else {
+                       else {
                                if (MP_FLAGS(frag) & MP_END_FRAG)
-                                       stats->frame_drops++;
-                               __skb_unlink(skb, &mp->frags);
+                                       stats->frame_drops++;
+                               if( mp->frags == frag )
+                                       mp->frags = nextf;      
                                isdn_ppp_mp_free_skb(mp, frag);
+                               frag = nextf;
                                continue;
-                       }
+                       }
                }
-
-               /* if we have end fragment, then we have full reassembly
-                * sequence -- reassemble and process packet now
+               
+               /* if start is non-null and we have end fragment, then
+                * we have full reassembly sequence -- reassemble 
+                * and process packet now
                 */
-               if (MP_FLAGS(frag) & MP_END_FRAG) {
-                       minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
-                       /* Reassemble the packet then dispatch it */
-                       isdn_ppp_mp_reassembly(net_dev, lp, start, frag, thisseq);
+               if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) {
+                       minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
+                       /* Reassemble the packet then dispatch it */
+                       isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);
+      
+                       start = NULL;
+                       frag = NULL;
 
-                       start = NULL;
-                       frag = NULL;
-               }
+                       mp->frags = nextf;
+               }
 
                /* check if need to update start pointer: if we just
                 * reassembled the packet and sequence is contiguous
@@ -1742,25 +1756,26 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
                 * below low watermark and set start to the next frag or
                 * clear start ptr.
                 */ 
-               if (nextf != (struct sk_buff *)&mp->frags && 
+               if (nextf != NULL && 
                    ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
-                       /* if we just reassembled and the next one is here, 
-                        * then start another reassembly.
-                        */
-                       if (frag == NULL) {
+                       /* if we just reassembled and the next one is here, 
+                        * then start another reassembly. */
+
+                       if (frag == NULL) {
                                if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
-                                       start = nextf;
-                               else {
-                                       printk(KERN_WARNING"isdn_mppp(seq %d):"
-                                              " END flag with no following "
-                                              "BEGIN", thisseq);
+                                       start = nextf;
+                               else
+                               {
+                                       printk(KERN_WARNING"isdn_mppp(seq %d):"
+                                               " END flag with no following "
+                                               "BEGIN", thisseq);
                                        stats->seqerrs++;
                                }
                        }
-               } else {
-                       if (nextf != (struct sk_buff *)&mp->frags &&
-                           frag != NULL &&
-                           MP_LT(thisseq, minseq)) {
+
+               } else {
+                       if ( nextf != NULL && frag != NULL &&
+                                               MP_LT(thisseq, minseq)) {
                                /* we've got a break in the sequence
                                 * and we not at the end yet
                                 * and we did not just reassembled
@@ -1769,39 +1784,41 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
                                 * discard all the frames below low watermark 
                                 * and start over */
                                stats->frame_drops++;
-                               isdn_ppp_mp_discard(mp, start, nextf);
+                               mp->frags = isdn_ppp_mp_discard(mp,start,nextf);
                        }
                        /* break in the sequence, no reassembly */
-                       start = NULL;
-               }
-               if (!start)
-                       break;
-       }
-
-check_overflow:
+                       start = NULL;
+               }
+                               
+               frag = nextf;
+       }       /* while -- main loop */
+       
+       if (mp->frags == NULL)
+               mp->frags = frag;
+               
        /* rather straighforward way to deal with (not very) possible 
-        * queue overflow
-        */
+        * queue overflow */
        if (mp->frames > MP_MAX_QUEUE_LEN) {
                stats->overflows++;
-               skb_queue_walk_safe(&mp->frags, frag, nextf) {
-                       if (mp->frames <= MP_MAX_QUEUE_LEN)
-                               break;
-                       __skb_unlink(frag, &mp->frags);
-                       isdn_ppp_mp_free_skb(mp, frag);
+               while (mp->frames > MP_MAX_QUEUE_LEN) {
+                       frag = mp->frags->next;
+                       isdn_ppp_mp_free_skb(mp, mp->frags);
+                       mp->frags = frag;
                }
        }
        spin_unlock_irqrestore(&mp->lock, flags);
 }
 
-static void isdn_ppp_mp_cleanup(isdn_net_local *lp)
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp )
 {
-       struct sk_buff *skb, *tmp;
-
-       skb_queue_walk_safe(&lp->netdev->pb->frags, skb, tmp) {
-               __skb_unlink(skb, &lp->netdev->pb->frags);
-               isdn_ppp_mp_free_skb(lp->netdev->pb, skb);
-       }
+       struct sk_buff * frag = lp->netdev->pb->frags;
+       struct sk_buff * nextfrag;
+       while( frag ) {
+               nextfrag = frag->next;
+               isdn_ppp_mp_free_skb(lp->netdev->pb, frag);
+               frag = nextfrag;
+       }
+       lp->netdev->pb->frags = NULL;
 }
 
 static u32 isdn_ppp_mp_get_seq( int short_seq, 
@@ -1838,115 +1855,72 @@ static u32 isdn_ppp_mp_get_seq( int short_seq,
        return seq;
 }
 
-static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from,
-                               struct sk_buff *to)
+struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+                       struct sk_buff * from, struct sk_buff * to )
 {
-       if (from) {
-               struct sk_buff *skb, *tmp;
-               int freeing = 0;
-
-               skb_queue_walk_safe(&mp->frags, skb, tmp) {
-                       if (skb == to)
-                               break;
-                       if (skb == from)
-                               freeing = 1;
-                       if (!freeing)
-                               continue;
-                       __skb_unlink(skb, &mp->frags);
-                       isdn_ppp_mp_free_skb(mp, skb);
+       if( from )
+               while (from != to) {
+                       struct sk_buff * next = from->next;
+                       isdn_ppp_mp_free_skb(mp, from);
+                       from = next;
                }
-       }
-}
-
-static unsigned int calc_tot_len(struct sk_buff_head *queue,
-                                struct sk_buff *from, struct sk_buff *to)
-{
-       unsigned int tot_len = 0;
-       struct sk_buff *skb;
-       int found_start = 0;
-
-       skb_queue_walk(queue, skb) {
-               if (skb == from)
-                       found_start = 1;
-               if (!found_start)
-                       continue;
-               tot_len += skb->len - MP_HEADER_LEN;
-               if (skb == to)
-                       break;
-       }
-       return tot_len;
+       return from;
 }
 
-/* Reassemble packet using fragments in the reassembly queue from
- * 'from' until 'to', inclusive.
- */
-static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp,
-                                  struct sk_buff *from, struct sk_buff *to,
-                                  u32 lastseq)
+void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
+                               struct sk_buff * from, struct sk_buff * to )
 {
-       ippp_bundle *mp = net_dev->pb;
-       unsigned int tot_len;
-       struct sk_buff *skb;
+       ippp_bundle * mp = net_dev->pb;
        int proto;
+       struct sk_buff * skb;
+       unsigned int tot_len;
 
        if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
                printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
                        __func__, lp->ppp_slot);
                return;
        }
-
-       tot_len = calc_tot_len(&mp->frags, from, to);
-
-       if (MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG)) {
-               if (ippp_table[lp->ppp_slot]->debug & 0x40)
+       if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
+               if( ippp_table[lp->ppp_slot]->debug & 0x40 )
                        printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, "
-                              "len %d\n", MP_SEQ(from), from->len);
+                                       "len %d\n", MP_SEQ(from), from->len );
                skb = from;
                skb_pull(skb, MP_HEADER_LEN);
-               __skb_unlink(skb, &mp->frags);
                mp->frames--;   
        } else {
-               struct sk_buff *walk, *tmp;
-               int found_start = 0;
+               struct sk_buff * frag;
+               int n;
 
-               if (ippp_table[lp->ppp_slot]->debug & 0x40)
-                       printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
-                              "to %d, len %d\n", MP_SEQ(from), lastseq,
-                              tot_len);
+               for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)
+                       tot_len += frag->len - MP_HEADER_LEN;
 
-               skb = dev_alloc_skb(tot_len);
-               if (!skb)
+               if( ippp_table[lp->ppp_slot]->debug & 0x40 )
+                       printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
+                               "to %d, len %d\n", MP_SEQ(from), 
+                               (MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );
+               if( (skb = dev_alloc_skb(tot_len)) == NULL ) {
                        printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
-                              "of size %d\n", tot_len);
-
-               found_start = 0;
-               skb_queue_walk_safe(&mp->frags, walk, tmp) {
-                       if (walk == from)
-                               found_start = 1;
-                       if (!found_start)
-                               continue;
+                                       "of size %d\n", tot_len);
+                       isdn_ppp_mp_discard(mp, from, to);
+                       return;
+               }
 
-                       if (skb) {
-                               unsigned int len = walk->len - MP_HEADER_LEN;
-                               skb_copy_from_linear_data_offset(walk, MP_HEADER_LEN,
-                                                                skb_put(skb, len),
-                                                                len);
-                       }
-                       __skb_unlink(walk, &mp->frags);
-                       isdn_ppp_mp_free_skb(mp, walk);
+               while( from != to ) {
+                       unsigned int len = from->len - MP_HEADER_LEN;
 
-                       if (walk == to)
-                               break;
+                       skb_copy_from_linear_data_offset(from, MP_HEADER_LEN,
+                                                        skb_put(skb,len),
+                                                        len);
+                       frag = from->next;
+                       isdn_ppp_mp_free_skb(mp, from);
+                       from = frag; 
                }
        }
-       if (!skb)
-               return;
-
        proto = isdn_ppp_strip_proto(skb);
        isdn_ppp_push_higher(net_dev, lp, skb, proto);
 }
 
-static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb)
+static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
 {
        dev_kfree_skb(skb);
        mp->frames--;
index 4c218ee..8687a7d 100644 (file)
@@ -157,7 +157,7 @@ typedef struct {
 
 typedef struct {
   int mp_mrru;                        /* unused                             */
-  struct sk_buff_head frags;   /* fragments sl list */
+  struct sk_buff * frags;      /* fragments sl list -- use skb->next */
   long frames;                 /* number of frames in the frame list */
   unsigned int seq;            /* last processed packet seq #: any packets
                                 * with smaller seq # will be dropped