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

This reverts commit 38783e6713.

It causes kernel bugzilla #14594

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2009-11-15 22:23:47 -08:00
parent 5bfa2a17f2
commit e29d436317
2 changed files with 162 additions and 188 deletions

View file

@ -1535,10 +1535,8 @@ static int isdn_ppp_mp_bundle_array_init(void)
int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle); int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL ) if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL )
return -ENOMEM; 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); spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
skb_queue_head_init(&isdn_ppp_bundle_arr[i].frags);
}
return 0; 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) if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
return -ENOMEM; return -ENOMEM;
lp->next = lp->last = lp; /* nobody else in a queue */ 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->frames = 0;
lp->netdev->pb->seq = UINT_MAX; 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, static u32 isdn_ppp_mp_get_seq( int short_seq,
struct sk_buff * skb, u32 last_seq ); struct sk_buff * skb, u32 last_seq );
static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from, static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
struct sk_buff *to); struct sk_buff * from, struct sk_buff * to );
static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp, static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
struct sk_buff *from, struct sk_buff *to, 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 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_print_recv_pkt( int slot, struct sk_buff * skb );
static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 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; 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; unsigned long flags;
isdn_net_local *lpq;
ippp_bundle *mp;
int slot; int slot;
spin_lock_irqsave(&net_dev->pb->lock, flags); spin_lock_irqsave(&net_dev->pb->lock, flags);
mp = net_dev->pb; mp = net_dev->pb;
stats = &mp->stats; stats = &mp->stats;
slot = lp->ppp_slot; slot = lp->ppp_slot;
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: lp->ppp_slot(%d)\n", 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; return;
} }
is = ippp_table[slot]; is = ippp_table[slot];
if (++mp->frames > stats->max_queue_len) if( ++mp->frames > stats->max_queue_len )
stats->max_queue_len = mp->frames; stats->max_queue_len = mp->frames;
if (is->debug & 0x8) if (is->debug & 0x8)
isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb); isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ,
skb, is->last_link_seqno); skb, is->last_link_seqno);
/* if this packet seq # is less than last already processed one, /* if this packet seq # is less than last already processed one,
* toss it right away, but check for sequence start case first * 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 mp->seq = newseq; /* the first packet: required for
* rfc1990 non-compliant clients -- * rfc1990 non-compliant clients --
* prevents constant packet toss */ * 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); spin_unlock_irqrestore(&mp->lock, flags);
return; return;
} }
/* find the minimum received sequence number over all links */ /* find the minimum received sequence number over all links */
is->last_link_seqno = minseq = newseq; is->last_link_seqno = minseq = newseq;
for (lpq = net_dev->queue;;) { 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 */ * packets */
newfrag = skb; newfrag = skb;
/* Insert new fragment into the proper sequence slot. */ /* if this new fragment is before the first one, then enqueue it now. */
skb_queue_walk(&mp->frags, frag) { if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) {
if (MP_SEQ(frag) == newseq) { newfrag->next = frag;
isdn_ppp_mp_free_skb(mp, newfrag); mp->frags = frag = newfrag;
newfrag = NULL; 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 &&
start = ((MP_FLAGS(frag) & MP_BEGIN_FRAG) && MP_SEQ(frag) == mp->seq ? frag : NULL;
(MP_SEQ(frag) == mp->seq)) ? frag : NULL;
if (!start)
goto check_overflow;
/* main fragment traversing loop /*
* main fragment traversing loop
* *
* try to accomplish several tasks: * 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' * - reassemble any complete fragment sequence (non-null 'start'
* indicates there is a continguous sequence present) * indicates there is a continguous sequence present)
* - discard any incomplete sequences that are below minseq -- due * - 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 * come to complete such sequence and it should be discarded
* *
* loop completes when we accomplished the following tasks: * 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 * - we hit a gap in the sequence, so no reassembly/processing is
* possible ('start' would be set to NULL) * possible ('start' would be set to NULL)
* *
* algorithm for this code is derived from code in the book * algorithm for this code is derived from code in the book
* 'PPP Design And Debugging' by James Carlson (Addison-Wesley) * '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);
/* check for misplaced start */ thisseq = MP_SEQ(frag);
if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) { nextf = frag->next;
printk(KERN_WARNING"isdn_mppp(seq %d): new "
"BEGIN flag with no prior END", thisseq); /* drop any duplicate fragments */
stats->seqerrs++; if (newfrag != NULL && thisseq == newseq) {
stats->frame_drops++; isdn_ppp_mp_free_skb(mp, newfrag);
isdn_ppp_mp_discard(mp, start, frag); newfrag = NULL;
start = frag; }
} else if (MP_LE(thisseq, minseq)) {
if (MP_FLAGS(frag) & MP_BEGIN_FRAG) /* 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; start = frag;
else { else {
if (MP_FLAGS(frag) & MP_END_FRAG) if (MP_FLAGS(frag) & MP_END_FRAG)
stats->frame_drops++; stats->frame_drops++;
__skb_unlink(skb, &mp->frags); if( mp->frags == frag )
mp->frags = nextf;
isdn_ppp_mp_free_skb(mp, frag); isdn_ppp_mp_free_skb(mp, frag);
frag = nextf;
continue; continue;
} }
} }
/* if we have end fragment, then we have full reassembly /* if start is non-null and we have end fragment, then
* sequence -- reassemble and process packet now * 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; minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
/* Reassemble the packet then dispatch it */ /* 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;
start = NULL; mp->frags = nextf;
frag = NULL; }
}
/* check if need to update start pointer: if we just /* check if need to update start pointer: if we just
* reassembled the packet and sequence is contiguous * 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 * below low watermark and set start to the next frag or
* clear start ptr. * clear start ptr.
*/ */
if (nextf != (struct sk_buff *)&mp->frags && if (nextf != NULL &&
((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) { ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
/* if we just reassembled and the next one is here, /* if we just reassembled and the next one is here,
* then start another reassembly. * then start another reassembly. */
*/
if (frag == NULL) { if (frag == NULL) {
if (MP_FLAGS(nextf) & MP_BEGIN_FRAG) if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
start = nextf; start = nextf;
else { else
printk(KERN_WARNING"isdn_mppp(seq %d):" {
" END flag with no following " printk(KERN_WARNING"isdn_mppp(seq %d):"
"BEGIN", thisseq); " END flag with no following "
"BEGIN", thisseq);
stats->seqerrs++; stats->seqerrs++;
} }
} }
} else {
if (nextf != (struct sk_buff *)&mp->frags && } else {
frag != NULL && if ( nextf != NULL && frag != NULL &&
MP_LT(thisseq, minseq)) { MP_LT(thisseq, minseq)) {
/* we've got a break in the sequence /* we've got a break in the sequence
* and we not at the end yet * and we not at the end yet
* and we did not just reassembled * 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 * discard all the frames below low watermark
* and start over */ * and start over */
stats->frame_drops++; 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 */ /* break in the sequence, no reassembly */
start = NULL; start = NULL;
} }
if (!start)
break; frag = nextf;
} } /* while -- main loop */
check_overflow: if (mp->frags == NULL)
mp->frags = frag;
/* rather straighforward way to deal with (not very) possible /* rather straighforward way to deal with (not very) possible
* queue overflow * queue overflow */
*/
if (mp->frames > MP_MAX_QUEUE_LEN) { if (mp->frames > MP_MAX_QUEUE_LEN) {
stats->overflows++; stats->overflows++;
skb_queue_walk_safe(&mp->frags, frag, nextf) { while (mp->frames > MP_MAX_QUEUE_LEN) {
if (mp->frames <= MP_MAX_QUEUE_LEN) frag = mp->frags->next;
break; isdn_ppp_mp_free_skb(mp, mp->frags);
__skb_unlink(frag, &mp->frags); mp->frags = frag;
isdn_ppp_mp_free_skb(mp, frag);
} }
} }
spin_unlock_irqrestore(&mp->lock, flags); 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; struct sk_buff * frag = lp->netdev->pb->frags;
struct sk_buff * nextfrag;
skb_queue_walk_safe(&lp->netdev->pb->frags, skb, tmp) { while( frag ) {
__skb_unlink(skb, &lp->netdev->pb->frags); nextfrag = frag->next;
isdn_ppp_mp_free_skb(lp->netdev->pb, skb); 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, 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; return seq;
} }
static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from, struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
struct sk_buff *to) struct sk_buff * from, struct sk_buff * to )
{ {
if (from) { if( from )
struct sk_buff *skb, *tmp; while (from != to) {
int freeing = 0; struct sk_buff * next = from->next;
isdn_ppp_mp_free_skb(mp, from);
skb_queue_walk_safe(&mp->frags, skb, tmp) { from = next;
if (skb == to)
break;
if (skb == from)
freeing = 1;
if (!freeing)
continue;
__skb_unlink(skb, &mp->frags);
isdn_ppp_mp_free_skb(mp, skb);
} }
} return from;
} }
static unsigned int calc_tot_len(struct sk_buff_head *queue, void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
struct sk_buff *from, struct sk_buff *to) struct sk_buff * from, struct sk_buff * to )
{ {
unsigned int tot_len = 0; ippp_bundle * mp = net_dev->pb;
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;
}
/* 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)
{
ippp_bundle *mp = net_dev->pb;
unsigned int tot_len;
struct sk_buff *skb;
int proto; int proto;
struct sk_buff * skb;
unsigned int tot_len;
if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
__func__, lp->ppp_slot); __func__, lp->ppp_slot);
return; return;
} }
if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
tot_len = calc_tot_len(&mp->frags, from, to); 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, " 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 = from;
skb_pull(skb, MP_HEADER_LEN); skb_pull(skb, MP_HEADER_LEN);
__skb_unlink(skb, &mp->frags);
mp->frames--; mp->frames--;
} else { } else {
struct sk_buff *walk, *tmp; struct sk_buff * frag;
int found_start = 0; int n;
if (ippp_table[lp->ppp_slot]->debug & 0x40) 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 " printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
"to %d, len %d\n", MP_SEQ(from), lastseq, "to %d, len %d\n", MP_SEQ(from),
tot_len); (MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );
if( (skb = dev_alloc_skb(tot_len)) == NULL ) {
skb = dev_alloc_skb(tot_len);
if (!skb)
printk(KERN_ERR "isdn_mppp: cannot allocate sk buff " printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
"of size %d\n", tot_len); "of size %d\n", tot_len);
isdn_ppp_mp_discard(mp, from, to);
return;
}
found_start = 0; while( from != to ) {
skb_queue_walk_safe(&mp->frags, walk, tmp) { unsigned int len = from->len - MP_HEADER_LEN;
if (walk == from)
found_start = 1;
if (!found_start)
continue;
if (skb) { skb_copy_from_linear_data_offset(from, MP_HEADER_LEN,
unsigned int len = walk->len - MP_HEADER_LEN; skb_put(skb,len),
skb_copy_from_linear_data_offset(walk, MP_HEADER_LEN, len);
skb_put(skb, len), frag = from->next;
len); isdn_ppp_mp_free_skb(mp, from);
} from = frag;
__skb_unlink(walk, &mp->frags);
isdn_ppp_mp_free_skb(mp, walk);
if (walk == to)
break;
} }
} }
if (!skb)
return;
proto = isdn_ppp_strip_proto(skb); proto = isdn_ppp_strip_proto(skb);
isdn_ppp_push_higher(net_dev, lp, skb, proto); 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); dev_kfree_skb(skb);
mp->frames--; mp->frames--;

View file

@ -157,7 +157,7 @@ typedef struct {
typedef struct { typedef struct {
int mp_mrru; /* unused */ 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 */ long frames; /* number of frames in the frame list */
unsigned int seq; /* last processed packet seq #: any packets unsigned int seq; /* last processed packet seq #: any packets
* with smaller seq # will be dropped * with smaller seq # will be dropped