Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e29d4363 authored by David S. Miller's avatar David S. Miller
Browse files

Revert "isdn: isdn_ppp: Use SKB list facilities instead of home-grown implementation."



This reverts commit 38783e67.

It causes kernel bugzilla #14594

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5bfa2a17
Loading
Loading
Loading
Loading
+163 −189
Original line number Diff line number Diff line
@@ -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,24 +1581,23 @@ 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 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,
				   u32 lastseq);
				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 *newfrag, *frag, *start, *nextf;
	u32 newseq, minseq, thisseq;
	isdn_mppp_stats *stats;
	struct ippp_struct *is;
	unsigned long flags;
	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;
	int slot;

	spin_lock_irqsave(&net_dev->pb->lock, flags);
@@ -1625,6 +1622,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
	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 
	 */
@@ -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);
  	/* 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;
			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);

	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,45 +1681,70 @@ 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) {
  	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++;
			isdn_ppp_mp_discard(mp, start, frag);
			start = frag;
				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 {
				if (MP_FLAGS(frag) & MP_END_FRAG)
	  				stats->frame_drops++;
				__skb_unlink(skb, &mp->frags);
				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) {
    		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, frag, thisseq);
			isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);
      
      			start = NULL;
      			frag = NULL;

      			mp->frags = nextf;
    		}

		/* check if need to update start pointer: if we just
@@ -1742,24 +1756,25 @@ 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.
			 */
			 * then start another reassembly. */

      			if (frag == NULL) {
				if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
	  				start = nextf;
				else {
				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 &&
			if ( nextf != NULL && frag != NULL &&
						MP_LT(thisseq, minseq)) {
				/* we've got a break in the sequence
				 * and we not at the end yet
@@ -1769,26 +1784,26 @@ 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:
    		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);
@@ -1796,12 +1811,14 @@ check_overflow:

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,110 +1855,67 @@ 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)
{
	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);
		}
	}
}

static unsigned int calc_tot_len(struct sk_buff_head *queue,
struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
			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;
	if( from )
		while (from != to) {
	  		struct sk_buff * next = from->next;
			isdn_ppp_mp_free_skb(mp, from);
	  		from = next;
		}
	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;
	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 )
			printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, "
					"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;

		for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)
			tot_len += frag->len - MP_HEADER_LEN;

		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);

		skb = dev_alloc_skb(tot_len);
		if (!skb)
				"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);
			isdn_ppp_mp_discard(mp, from, to);
			return;
		}

		found_start = 0;
		skb_queue_walk_safe(&mp->frags, walk, tmp) {
			if (walk == from)
				found_start = 1;
			if (!found_start)
				continue;
		while( from != to ) {
			unsigned int len = from->len - MP_HEADER_LEN;

			if (skb) {
				unsigned int len = walk->len - MP_HEADER_LEN;
				skb_copy_from_linear_data_offset(walk, MP_HEADER_LEN,
			skb_copy_from_linear_data_offset(from, MP_HEADER_LEN,
							 skb_put(skb,len),
							 len);
			}
			__skb_unlink(walk, &mp->frags);
			isdn_ppp_mp_free_skb(mp, walk);

			if (walk == to)
				break;
			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);
}
+1 −1
Original line number Diff line number Diff line
@@ -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