lkml.org 
[lkml]   [2000]   [Mar]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] isdn multilink ppp receive-2.2.14
Hi,

This is a patch/rewrite for the isdn multilink PPP receive code, which
you will need if you servicing numerous isdn MLPPP calls.
It fixes various bundling/reassembly problems (i.e. Oopses) with the
original code and should be more efficient as well. It does packet
reordering and defragmentation.
This code has been tested on simulator (available if anybody interested)
and then on real system with 60 ISDN channels and both linux and NT
clients
with bundles up to 10 channels. For testing we used
isdn4k-utils.v3.1beta7,
ipppd with +mp option and no explicit channel bundling (on a server,
that is).

This patch applies to 2.2.14 kernel.

Send comments, flames to <semyon.sosin@ariel.com>

--- linux-2.2.14.mp/include/linux/isdn.h.orig Fri Mar 10 18:31:05 2000
+++ linux-2.2.14.mp/include/linux/isdn.h Fri Mar 10 18:31:06 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.81 1999/10/27 21:21:18 detabc Exp $
+/* $Id: isdn.h,v 1.81-mp1 2000/03/02 11:44:22 semyon Exp $
*
* Main header for the Linux ISDN subsystem (linklevel).
*
@@ -21,6 +21,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn.h,v $
+ * Revision 1.81-mp1 1999/10/27 21:21:18 semyon@ariel.com
+ * server-side multilink code rewrite
+ *
* Revision 1.81 1999/10/27 21:21:18 detabc
* Added support for building logically-bind-group's per interface.
* usefull for outgoing call's with more then one isdn-card.
@@ -672,8 +675,8 @@
void *next; /* Pointer to next
isdn-interface */
struct device dev; /* interface to upper levels
*/
#ifdef CONFIG_ISDN_PPP
- struct mpqueue *mp_last;
- struct ippp_bundle ib;
+ ippp_bundle * pb; /* pointer to the common bundle structure
+ * with the the per-bundle data */
#endif
#ifdef CONFIG_ISDN_X25
struct concap_proto *cprot; /* connection oriented encapsulation
protocol */
--- linux-2.2.14.mp/include/linux/isdn_ppp.h.orig Fri Mar 10 18:31:06
2000
+++ linux-2.2.14.mp/include/linux/isdn_ppp.h Fri Mar 10 18:31:06 2000
@@ -52,6 +52,8 @@
#define MP_END_FRAG 0x40
#define MP_BEGIN_FRAG 0x80

+#define MP_MAX_QUEUE_LEN 16
+
#define ISDN_PPP_COMP_MAX_OPTIONS 16

#define IPPP_COMP_FLAG_XMIT 0x1
@@ -66,6 +68,8 @@

#ifdef __KERNEL__

+#include <asm/semaphore.h>
+
/*
* We need a way for the decompressor to influence the generation of
CCP
* Reset-Requests in a variety of ways. The decompressor is already
returning
@@ -134,35 +138,28 @@
extern int isdn_ppp_dial_slave(char *);
extern int isdn_ppp_hangup_slave(char *);

-struct ippp_bundle {
+typedef struct {
+ unsigned long seqerrs;
+ unsigned long frame_drops;
+ unsigned long overflows;
+ unsigned long max_queue_len;
+} isdn_mppp_stats;
+
+typedef struct {
int mp_mrru; /*
unused */
- struct mpqueue *last; /* currently defined in
isdn_net_dev */
- int min; /* currently calculated 'on the
fly' */
- long next_num; /* we wanna see this seq.-number
next */
- struct sqqueue *sq;
- int modify:1; /* set to 1 while modifying
sqqueue */
- int bundled:1; /* bundle active
? */
-};
+ 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
+ * unconditionally */
+ struct semaphore lock;
+ int ref_ct;
+ /* statistics */
+ isdn_mppp_stats stats;
+} ippp_bundle;

#define NUM_RCV_BUFFS 64

-struct sqqueue {
- struct sqqueue *next;
- long sqno_start;
- long sqno_end;
- struct sk_buff *skb;
- long timer;
-};
-
-struct mpqueue {
- struct mpqueue *next;
- struct mpqueue *last;
- long sqno;
- struct sk_buff *skb;
- int BEbyte;
- unsigned long time;
-};
-
struct ippp_buf_queue {
struct ippp_buf_queue *next;
struct ippp_buf_queue *last;
@@ -217,9 +214,8 @@
struct isdn_net_local_s *lp;
int unit;
int minor;
- long last_link_seqno;
+ unsigned int last_link_seqno;
long mp_seqno;
- long range;
#ifdef CONFIG_ISDN_PPP_VJ
unsigned char *cbuf;
struct slcompress *slcomp;
--- linux-2.2.14.mp/drivers/isdn/isdn_ppp.c.orig Fri Mar 10 18:31:09
2000
+++ linux-2.2.14.mp/drivers/isdn/isdn_ppp.c Fri Mar 10 18:31:10 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.60 1999/11/04 20:29:55 he Exp $
+/* $Id: isdn_ppp.c,v 1.60-mp1 2000/03/01 01:03:19 he Exp $
*
* Linux ISDN subsystem, functions for synchronous PPP (linklevel).
*
@@ -19,6 +19,10 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_ppp.c,v $
+ *
+ * Revision 1.60-mp1 2000/02/27 22:55:44 semyon@ariel.com
+ * server-side multilink code rewrite
+ *
* Revision 1.60 1999/11/04 20:29:55 he
* applied Andre Beck's reset_free fix
*
@@ -296,17 +300,21 @@


#ifdef CONFIG_ISDN_MPP
+static ippp_bundle * isdn_ppp_bundle_arr = NULL;
+
+static int isdn_ppp_mp_bundle_array_init(void);
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to
);
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local
* lp,
+ struct sk_buff *skb);
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
+
static int isdn_ppp_bundle(struct ippp_struct *, int unit);
-static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
-static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min);
-static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev, isdn_net_local
*, long min);
-static void isdn_ppp_free_sqqueue(isdn_net_dev *);
-static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
- int BEbyte, long *sqno, int min_sqno);
-static void isdn_ppp_free_mpqueue(isdn_net_dev *);
-#endif

-char *isdn_ppp_revision = "$Revision: 1.60 $";
+#define MP_LOCK(b) down(&(b)->lock)
+#define MP_UNLOCK(b) up(&(b)->lock)
+#endif /* CONFIG_ISDN_MPP */
+
+char *isdn_ppp_revision = "$Revision: 1.60-mp1 $";

static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];

@@ -342,10 +350,11 @@
isdn_ppp_free(isdn_net_local * lp)
{
#ifdef CONFIG_ISDN_MPP
- isdn_net_local *master_lp = lp;
+ isdn_net_local *master_lp = NULL;
#endif
unsigned long flags;
struct ippp_struct *is;
+ isdn_net_local * nlp;

if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS)
return 0;
@@ -355,21 +364,39 @@
save_flags(flags);
cli();
#ifdef CONFIG_ISDN_MPP
+ MP_LOCK(lp->netdev->pb);
+
if (lp->master)
master_lp = (isdn_net_local *) lp->master->priv;

+
+ /* make sure none of the queue pointers will point to the
+ * interface being removed */
+ for (nlp=lp->next; nlp != lp; nlp=nlp->next)
+ if (nlp->netdev->queue == lp )
+ nlp->netdev->queue = lp->next;
+
lp->last->next = lp->next;
lp->next->last = lp->last;
- if (master_lp->netdev->queue == lp) {
+
+ if (master_lp && master_lp->netdev->queue == lp)
master_lp->netdev->queue = lp->next;
- if (lp->next == lp) { /* last link in queue? */
- master_lp->netdev->ib.bundled = 0;
- isdn_ppp_free_mpqueue(master_lp->netdev);
- isdn_ppp_free_sqqueue(master_lp->netdev);
- }
- }
+/*
+ if (lp->next->netdev->queue == lp)
+ lp->next->netdev->queue = lp->next;
+ if (lp->last->netdev->queue == lp)
+ lp->last->netdev->queue = lp->last;
+*/
+ if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */
+ isdn_ppp_mp_cleanup(lp);
+
lp->next = lp->last = lp; /* (re)set own pointers */
-#endif
+ lp->netdev->queue = lp;
+
+ MP_UNLOCK(lp->netdev->pb);
+ lp->netdev->pb->ref_ct--;
+
+#endif /* CONFIG_ISDN_MPP */

if ((is->state & IPPP_CONNECT))
isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */
@@ -439,20 +466,17 @@
printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",
lp->name);
return -1;
}
+
lp->ppp_slot = i;

- /* reset some values */
- lp->netdev->ib.bundled = 0;
- lp->netdev->ib.next_num = 0;
- lp->netdev->ib.modify = 0;
- lp->netdev->ib.last = NULL;
- lp->netdev->ib.min = 0;
- lp->netdev->ib.sq = NULL;
-
is = ippp_table[i];
is->lp = lp;
is->unit = unit;
is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but
not connected */
+#ifdef CONFIG_ISDN_MPP
+ if (isdn_ppp_mp_init(lp, NULL) < 0)
+ return -ENOMEM;
+#endif /* CONFIG_ISDN_MPP */

restore_flags(flags);

@@ -471,7 +495,7 @@
return;

ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT |
IPPP_NOBLOCK;
-
+
if (ippp_table[lp->ppp_slot]->wq)
wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
}
@@ -546,7 +570,6 @@
is->mp_seqno = 0; /* MP sequence number */
is->pppcfg = 0; /* ppp configuration */
is->mpppcfg = 0; /* mppp configuration */
- is->range = 0x1000000; /* MP: 24 bit range */
is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining
a bundle ?? */
is->unit = -1; /* set, when we have our interface */
is->mru = 1524; /* MRU, default 1524 */
@@ -1056,6 +1079,11 @@
{
int i,
j;
+
+#ifdef CONFIG_ISDN_MPP
+ if( isdn_ppp_mp_bundle_array_init() < 0 )
+ return -ENOMEM;
+#endif /* CONFIG_ISDN_MPP */

for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
if (!(ippp_table[i] = (struct ippp_struct *)
@@ -1087,6 +1115,12 @@

for (i = 0; i < ISDN_MAX_CHANNELS; i++)
kfree(ippp_table[i]);
+
+#ifdef CONFIG_ISDN_MPP
+ if (isdn_ppp_bundle_arr)
+ kfree(isdn_ppp_bundle_arr);
+#endif /* CONFIG_ISDN_MPP */
+
}

/*
@@ -1136,7 +1170,6 @@

#ifdef CONFIG_ISDN_MPP
if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
- int sqno_end;

if(is->compflags & SC_LINK_DECOMP_ON) {
if(proto == PPP_LINK_COMP) {
@@ -1152,150 +1185,12 @@
}

if (proto == PPP_MP) {
- isdn_net_local *lpq;
- long sqno, min_sqno, tseq;
-
- u_char BEbyte = skb->data[0];
- if (is->debug & 0x8)
- printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x
%02x\n", lp->ppp_slot, proto,
- (int) skb->len, (int) skb->data[0], (int) skb->data[1],
(int) skb->data[2],
- (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
- if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) {
- sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) +
(int) skb->data[3];
- skb_pull(skb, 4);
- } else {
- sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
- skb_pull(skb, 2);
- }
-
- /*
- * new sequence number lower than last number? (this is only allowed
- * for overflow case)
- */
- if ((tseq = is->last_link_seqno) >= sqno) {
- int range = is->range;
- if (tseq + 1024 < range + sqno) /* redundancy check .. not MP
conform */
- printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with
sqno: %ld, last: %ld !!!\n", sqno, tseq);
- else {
- sqno += range;
- is->last_link_seqno = sqno;
- }
- } else {
- /* here, we should also add an redundancy check */
- is->last_link_seqno = sqno;
- }
-
- /*
- * step over all links to find lowest link number
- */
- for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) {
- long lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
- if (lls >= 0 && lls < min_sqno)
- min_sqno = lls;
- lpq = lpq->next;
- if (lpq == net_dev->queue)
- break;
- }
-
- /*
- * for the case, that the last frame numbers of all
- * links are overflowed: mask/reduce the sequenece number to
- * 'normal' numbering.
- */
- if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {
- int mask = ippp_table[lpq->ppp_slot]->range-1; /* range is power of
two, so a mask will do the job */
- isdn_ppp_mask_queue(net_dev, mask);
- net_dev->ib.next_num &= mask;
- {
- struct sqqueue *q = net_dev->ib.sq;
- while (q) {
- q->sqno_start &= mask;
- q->sqno_end &= mask;
- }
- }
- min_sqno &= mask;
- for (lpq = net_dev->queue;;) {
- if(ippp_table[lpq->ppp_slot]->last_link_seqno >= 0)
- ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
- lpq = lpq->next;
- if (lpq == net_dev->queue)
- break;
- }
- }
- if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG |
MP_END_FRAG)) {
- static int dmes = 0;
- if( !dmes ) {
- printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d ..
UNTESTED!!\n", lp->ppp_slot);
- dmes = 1;
- }
- if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb, BEbyte, &sqno,
min_sqno)) < 0) {
- net_dev->ib.modify = 1; /* block timeout-timer */
- isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
- net_dev->ib.modify = 0;
- return; /* no packet complete */
- }
- } else
- sqno_end = sqno;
-
- if (is->debug & 0x40)
- printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n",
min_sqno, sqno_end, net_dev->ib.next_num);
-
- /*
- * MP buffer management .. reorders incoming packets ..
- * lotsa mem-copies and not heavily tested.
- *
- * first check whether there is more than one link in the bundle
- * then check whether the number is in order
- */
- net_dev->ib.modify = 1; /* block timeout-timer */
- if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
- /*
- * packet is not 'in order'
- */
- struct sqqueue *q;
-
- q = (struct sqqueue *) kmalloc(sizeof(struct sqqueue), GFP_ATOMIC);
- if (!q) {
- net_dev->ib.modify = 0;
- printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
- dev_kfree_skb(skb);
- return; /* discard */
- }
- q->skb = skb;
- q->sqno_end = sqno_end;
- q->sqno_start = sqno;
- q->timer = jiffies + (ISDN_TIMER_1SEC) * 5; /* timeout after 5
seconds */
-
- if (!net_dev->ib.sq) {
- net_dev->ib.sq = q;
- q->next = NULL;
- } else {
- struct sqqueue *ql = net_dev->ib.sq;
- if (ql->sqno_start > q->sqno_start) {
- q->next = ql;
- net_dev->ib.sq = q;
- } else {
- while (ql->next && ql->next->sqno_start < q->sqno_start)
- ql = ql->next;
- q->next = ql->next;
- ql->next = q;
- }
- }
- } else {
- /*
- * packet was 'in order' .. push it higher
- */
- net_dev->ib.next_num = sqno_end + 1;
- proto = isdn_ppp_strip_proto(skb);
- isdn_ppp_push_higher(net_dev, lp, skb, proto);
- }
- isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
- net_dev->ib.modify = 0;
-
- } else
+ isdn_ppp_mp_receive(net_dev, lp, skb);
+ }
+ else
isdn_ppp_push_higher(net_dev, lp, skb, proto);
} else
-#endif
+#endif /* CONFIG_ISDN_MPP */
isdn_ppp_push_higher(net_dev, lp, skb, proto);
}

@@ -1660,71 +1555,447 @@

#ifdef CONFIG_ISDN_MPP

-/*
- * free SQ queue
- * -------------
- * Note: We need two queues for MPPP. The SQ queue holds fully
(re)assembled frames,
- * that can't be delivered, because there is an outstanding earlier
frame
- */
-static void
-isdn_ppp_free_sqqueue(isdn_net_dev * p)
+/* this is _not_ rfc1990 header, but something we convert both short
and long
+ * headers to for convinience's sake:
+ * byte 0 is flags as in rfc1990
+ * bytes 1...4 is 24-bit seqence number converted to host byte order
+ */
+#define MP_HEADER_LEN 5
+
+#define MP_LONGSEQ_MASK 0x00ffffff
+#define MP_SHORTSEQ_MASK 0x00000fff
+#define MP_LONGSEQ_MAX MP_LONGSEQ_MASK
+#define MP_SHORTSEQ_MAX MP_SHORTSEQ_MASK
+#define MP_LONGSEQ_MAXBIT ((MP_LONGSEQ_MASK+1)>>1)
+#define MP_SHORTSEQ_MAXBIT ((MP_SHORTSEQ_MASK+1)>>1)
+
+/* sequence-wrap safe comparisions (for long sequence)*/
+#define MP_LT(a,b) ((a-b)&MP_LONGSEQ_MAXBIT)
+#define MP_LE(a,b) !((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GT(a,b) ((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GE(a,b) !((a-b)&MP_LONGSEQ_MAXBIT)
+
+#define MP_SEQ(f) ((*(u32*)(f->data+1)))
+#define MP_FLAGS(f) (f->data[0])
+
+static int isdn_ppp_mp_bundle_array_init(void)
+{
+ int i;
+ int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
+ if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz,
+ GFP_KERNEL)) == NULL )
+ return -ENOMEM;
+ memset(isdn_ppp_bundle_arr, 0, sz);
+ for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
+ isdn_ppp_bundle_arr[i].lock = MUTEX;
+ return 0;
+}
+
+static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
{
- struct sqqueue *q = p->ib.sq;
+ int i;
+ for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
+ if (isdn_ppp_bundle_arr[i].ref_ct <= 0)
+ return (isdn_ppp_bundle_arr + i);
+ return NULL;
+}
+
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to
)
+{
+ struct ippp_struct * is = ippp_table[lp->ppp_slot];
+
+ if (add_to) {
+ if( lp->netdev->pb )
+ lp->netdev->pb->ref_ct--;
+ lp->netdev->pb = add_to;
+ } else { /* first link in a bundle */
+ is->mp_seqno = 0;
+ if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
+ return -ENOMEM;
+ lp->next = lp->last = lp; /* nobody else in a queue */
+ lp->netdev->pb->frags = NULL;
+ lp->netdev->pb->frames = 0;
+ lp->netdev->pb->seq = LONG_MAX;
+ }
+ lp->netdev->pb->ref_ct++;
+
+ is->last_link_seqno = 0;
+ return 0;
+}

- p->ib.sq = NULL;
- while (q) {
- struct sqqueue *qn = q->next;
- if (q->skb)
- dev_kfree_skb(q->skb);
- kfree(q);
- q = qn;
+static u32 isdn_ppp_mp_get_seq( int short_seq,
+ struct sk_buff * skb, u32 last_seq );
+struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+ struct sk_buff * from, struct sk_buff * to );
+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 ippp_struct *is = ippp_table[lp->ppp_slot];
+ isdn_net_local * lpq;
+ ippp_bundle * mp;
+ isdn_mppp_stats * stats;
+ struct sk_buff * newfrag, * frag, * start, *nextf;
+ u32 newseq, minseq, thisseq;
+
+ MP_LOCK(net_dev->pb);
+ mp = net_dev->pb;
+ stats = &mp->stats;
+
+ 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);
+
+
+ /* 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) ) {
+ mp->seq = newseq; /* the first packet: required for
+ * rfc1990 non-compliant clients --
+ * prevents constant packet toss */
+ } else if( MP_LT(newseq, mp->seq) ) {
+ stats->frame_drops++;
+ isdn_ppp_mp_free_skb(mp, skb);
+ MP_UNLOCK(mp);
+ return;
}
+
+ /* find the minimum received sequence number over all links */
+ is->last_link_seqno = minseq = newseq;
+ for (lpq = net_dev->queue;;) {
+ u32 lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
+ if (MP_LT(lls, minseq))
+ minseq = lls;
+ if ((lpq = lpq->next) == net_dev->queue)
+ break;
+ }
+ if (MP_LT(minseq, mp->seq))
+ minseq = mp->seq; /* can't go beyond already processed
+ * packets */
+ newfrag = skb;
+
+ /* 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;
+ }
+
+ start = MP_FLAGS(frag) & MP_BEGIN_FRAG &&
+ MP_SEQ(frag) == mp->seq ? frag : NULL;
+
+ /*
+ * 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
+ * to the fact that sender always increment sequence number, if
there
+ * is an incomplete sequence below minseq, no new fragments would
+ * 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)
+ *
+ * algorightm for this code is derived from code in the book
+ * 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
+ */
+ 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 {
+ if (MP_FLAGS(frag) & MP_END_FRAG)
+ stats->frame_drops++;
+ if( mp->frags == frag )
+ mp->frags = nextf;
+ isdn_ppp_mp_free_skb(mp, frag);
+ frag = nextf;
+ continue;
+ }
+ }
+
+ /* if start is non-null and we have end fragment, then
+ * we have full reassembly sequence -- reassemble
+ * and process packet now
+ */
+ 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;
+
+ mp->frags = nextf;
+ }
+
+ /* check if need to update start pointer: if we just
+ * reassembled the packet and sequence is contiguous
+ * then next fragment should be the start of new reassembly
+ * if sequence is contiguous, but we haven't reassembled yet,
+ * keep going.
+ * if sequence is not contiguous, either clear everyting
+ * below low watermark and set start to the next frag or
+ * clear start ptr.
+ */
+ 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 (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
+ start = nextf;
+ else
+ {
+ printk(KERN_WARNING"isdn_mppp(seq %d):"
+ " END flag with no following "
+ "BEGIN", thisseq);
+ stats->seqerrs++;
+ }
+ }

+ } 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
+ *(if we did, there wouldn't be anything before)
+ * and we below the low watermark
+ * discard all the frames below low watermark
+ * and start over */
+ stats->frame_drops++;
+ mp->frags = isdn_ppp_mp_discard(mp,start,nextf);
+ }
+ /* break in the sequence, no reassembly */
+ 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 */
+ if (mp->frames > MP_MAX_QUEUE_LEN) {
+ stats->overflows++;
+ while (mp->frames < MP_MAX_QUEUE_LEN) {
+ frag = mp->frags->next;
+ isdn_ppp_mp_free_skb(mp, mp->frags);
+ mp->frags = frag;
+ }
+ }
+
+ MP_UNLOCK(mp);
+}
+
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp )
+{
+ 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,
+ struct sk_buff * skb, u32 last_seq )
+{
+ u32 seq;
+ int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);
+
+ if( !short_seq )
+ {
+ seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK;
+ skb_push(skb,1);
+ }
+ else
+ {
+ /* convert 12-bit short seq number to 24-bit long one
+ */
+ seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK;
+
+ /* check for seqence wrap */
+ if( !(seq & MP_SHORTSEQ_MAXBIT) &&
+ (last_seq & MP_SHORTSEQ_MAXBIT) &&
+ (unsigned long)last_seq <= MP_LONGSEQ_MAX )
+ seq |= (last_seq + MP_SHORTSEQ_MAX+1) &
+ (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+ else
+ seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+
+ skb_push(skb, 3); /* put converted seqence back in skb */
+ }
+ *(u32*)(skb->data+1) = seq; /* put seqence back in _host_ byte
+ * order */
+ skb->data[0] = flags; /* restore flags */
+ return seq;
+}
+
+struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+ struct sk_buff * from, struct sk_buff * to )
+{
+ if( from )
+ while (from != to) {
+ struct sk_buff * next = from->next;
+ isdn_ppp_mp_free_skb(mp, from);
+ from = next;
+ }
+ return from;
+}
+
+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;
+ int proto;
+ struct sk_buff * skb;
+ unsigned int tot_len;
+
+ 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);
+ mp->frames--;
+ }
+ else
+ {
+ struct sk_buff * frag;
+ int n;
+
+ for( tot_len=0, frag=from, n = 0; 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),
+ (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;
+ }
+
+ while( from != to )
+ {
+ unsigned int len = from->len - MP_HEADER_LEN;
+ memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
+ frag = from->next;
+ isdn_ppp_mp_free_skb(mp, from);
+ from = frag;
+ }
+ }
+ proto = isdn_ppp_strip_proto(skb);
+ isdn_ppp_push_higher(net_dev, lp, skb, proto);
}

-/*
- * free MP queue
- * -------------
- * Note: The MP queue holds all frame fragments of frames, that can't
be
- * reassembled, because there is at least one missing fragment.
- */
-static void
-isdn_ppp_free_mpqueue(isdn_net_dev * p)
+static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff *
skb)
{
- struct mpqueue *q = p->mp_last;
- p->mp_last = NULL;
+ dev_kfree_skb(skb);
+ mp->frames--;
+}

- while (q) {
- struct mpqueue *ql = q->next;
- dev_kfree_skb(q->skb);
- kfree(q);
- q = ql;
- }
+static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb
)
+{
+ printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n",
+ slot, (int) skb->len,
+ (int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
+ (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
}

static int
isdn_ppp_bundle(struct ippp_struct *is, int unit)
{
char ifn[IFNAMSIZ + 1];
- long flags;
isdn_net_dev *p;
isdn_net_local *lp,
*nlp;
+ int rc;

sprintf(ifn, "ippp%d", unit);
p = isdn_net_findif(ifn);
if (!p)
- return -1;
+ {
+ printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);
+ return -EINVAL;
+ }

isdn_timer_ctrl(ISDN_TIMER_IPPP, 1); /* enable timer for ippp/MP */

- save_flags(flags);
- cli();
+ MP_LOCK(p->pb);

nlp = is->lp;

lp = p->queue;
- p->ib.bundled = 1;
+
+ if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ||
+ lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS )
+ {
+ MP_UNLOCK(p->pb);
+ printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
+ nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ?
+ nlp->ppp_slot : lp->ppp_slot );
+ return -EINVAL;
+ }
+
nlp->last = lp->last;
lp->last->next = nlp;
lp->last = nlp;
@@ -1739,229 +2010,14 @@
ippp_table[nlp->ppp_slot]->mpppcfg |=
ippp_table[lp->ppp_slot]->mpppcfg &
(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ |
SC_IN_SHORT_SEQ);

- restore_flags(flags);
- return 0;
-}
-
-/*
- * Mask sequence numbers in MP queue
- */
-static void
-isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
-{
- struct mpqueue *q = dev->mp_last;
- while (q) {
- q->sqno &= mask;
- q = q->next;
- }
-}
-
-/*
- * put a fragment at the right place into the MP queue
- * Also checks, whether this fragment completes a frame. In this case
- * the fragments are copied together into one SKB
- */
-static int
-isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int
BEbyte, long *sqnop, int min_sqno)
-{
- struct mpqueue *qe,
- *q1,
- *q;
- long cnt,
- flags;
- int pktlen,
- sqno_end;
- int sqno = *sqnop;
-
- q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_ATOMIC);
- if (!q1) {
- printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct
memory.\n");
- save_flags(flags);
- cli();
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
- return -1;
- }
- q1->skb = *skb;
- q1->sqno = sqno;
- q1->BEbyte = BEbyte;
- q1->time = jiffies;
-
- save_flags(flags);
- cli();
-
- if (!(q = dev->mp_last)) {
- dev->mp_last = q1;
- q1->next = NULL;
- q1->last = NULL;
- isdn_ppp_cleanup_mpqueue(dev, min_sqno); /* not necessary */
- restore_flags(flags);
- return -1; /* -1 is not an error. Just says, that this fragment
hasn't complete a full frame */
- }
- for (;;) { /* the faster way would be to step from the
queue-end to the start */
- if (sqno > q->sqno) {
- if (q->next) {
- q = q->next;
- continue;
- }
- q->next = q1;
- q1->next = NULL;
- q1->last = q;
- break;
- }
- if (sqno == q->sqno)
- printk(KERN_WARNING "isdn_fill_mpqueue: illegal sqno received!!\n");
- q1->last = q->last;
- q1->next = q;
- if (q->last) {
- q->last->next = q1;
- } else
- dev->mp_last = q1;
- q->last = q1;
- break;
- }
-
-/* now we check whether we completed a packet with this fragment */
- pktlen = -q1->skb->len;
- q = q1;
- cnt = q1->sqno;
- while (!(q->BEbyte & MP_END_FRAG)) {
- cnt++;
- if (!(q->next) || q->next->sqno != cnt) {
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
- return -1;
- }
- pktlen += q->skb->len;
- q = q->next;
- }
- pktlen += q->skb->len;
- qe = q;
-
- q = q1;
- cnt = q1->sqno;
- while (!(q->BEbyte & MP_BEGIN_FRAG)) {
- cnt--;
- if (!(q->last) || q->last->sqno != cnt) {
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
- return -1;
- }
- pktlen += q->skb->len;
- q = q->last;
- }
- pktlen += q->skb->len;
-
- if (q->last)
- q->last->next = qe->next;
- else
- dev->mp_last = qe->next;
-
- if (qe->next)
- qe->next->last = q->last;
- qe->next = NULL;
- sqno_end = qe->sqno;
- *sqnop = q->sqno;
-
- isdn_ppp_cleanup_mpqueue(dev, min_sqno);
- restore_flags(flags);
-
- *skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ
compression .. */
+ rc = isdn_ppp_mp_init(nlp, p->pb);

- if (!(*skb)) {
- while (q) {
- struct mpqueue *ql = q->next;
- dev_kfree_skb(q->skb);
- kfree(q);
- q = ql;
- }
- return -2;
- }
- cnt = 0;
- skb_put(*skb, pktlen);
- while (q) {
- struct mpqueue *ql = q->next;
- memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
- cnt += q->skb->len;
- dev_kfree_skb(q->skb);
- kfree(q);
- q = ql;
- }
+ MP_UNLOCK(p->pb);

- return sqno_end;
+ return rc;
}

-/*
- * check sq-queue, whether we have still buffered the next packet(s)
- * or packets with a sqno less or equal to min_sqno
- * net_dev: master netdevice , lp: 'real' local connection
- */
-static void
-isdn_ppp_cleanup_sqqueue(isdn_net_dev * net_dev, isdn_net_local * lp,
long min_sqno)
-{
- struct sqqueue *q;
-
- while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num
|| q->sqno_end <= min_sqno)) {
- int proto;
- if (q->sqno_start != net_dev->ib.next_num) {
- printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n",
net_dev->ib.next_num);
-#ifdef CONFIG_ISDN_PPP_VJ
- slhc_toss(ippp_table[net_dev->local->ppp_slot]->slcomp);
-#endif
- }
- proto = isdn_ppp_strip_proto(q->skb);
- isdn_ppp_push_higher(net_dev, lp, q->skb, proto);
- net_dev->ib.sq = q->next;
- net_dev->ib.next_num = q->sqno_end + 1;
- kfree(q);
- }
-}
-
-/*
- * remove stale packets from list
- */
-static void
-isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno)
-{
-#ifdef CONFIG_ISDN_PPP_VJ
- int toss = 0;
-#endif
-/* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
- eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch
rauswerfen
- wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen
nicht mehr gefuellt werden)
- bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber
pktlen der rumhaengenden Pakete
- groesser als mrru ist: raus damit , Pakete muessen allerdings
zusammenhaengen sonst koennte
- ja ein Paket mit B und eins mit E dazwischenpassen */
-
- struct mpqueue *ql,
- *q = dev->mp_last;
- while(q && (q->sqno < min_sqno) ) {
- if ( (q->BEbyte & MP_END_FRAG) ||
- (q->next && (q->next->sqno <= min_sqno) && (q->next->BEbyte &
MP_BEGIN_FRAG)) ) {
- printk(KERN_DEBUG "ippp: freeing stale packet(s), min_sq:
%ld!\n",min_sqno);
- if ((dev->mp_last = q->next))
- q->next->last = NULL;
- while (q) {
- ql = q->last;
- printk(KERN_DEBUG "ippp, freeing packet with sqno: %ld\n",q->sqno);
- dev_kfree_skb(q->skb);
- kfree(q);
-#ifdef CONFIG_ISDN_PPP_VJ
- toss = 1;
-#endif
- q = ql;
- }
- q = dev->mp_last;
- } else
- q = q->next;
- }
-#ifdef CONFIG_ISDN_PPP_VJ
- /* did we free a stale frame ? */
- if (toss)
- slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp);
-#endif
-}
-#endif
+#endif /* CONFIG_ISDN_MPP */

/*
* a buffered packet timed-out?
@@ -1970,44 +2026,7 @@
isdn_ppp_timer_timeout(void)
{
#ifdef CONFIG_ISDN_MPP
- isdn_net_dev *net_dev = dev->netdev;
- struct sqqueue *q,
- *ql = NULL,
- *qn;
-
- while (net_dev) {
- isdn_net_local *lp = net_dev->local;
- if (net_dev->ib.modify || lp->master) { /* interface locked or slave?
*/
- net_dev = net_dev->next;
- continue;
- }
- q = net_dev->ib.sq;
- while (q) {
- if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
-
-#ifdef CONFIG_ISDN_PPP_VJ
- /* did we step over a missing frame ? */
- if (q->sqno_start != net_dev->ib.next_num)
- slhc_toss(ippp_table[lp->ppp_slot]->slcomp);
-#endif
-
- ql = net_dev->ib.sq;
- net_dev->ib.sq = q->next;
- net_dev->ib.next_num = q->sqno_end + 1;
- q->next = NULL;
- for (; ql;) {
- int proto = isdn_ppp_strip_proto(ql->skb);
- isdn_ppp_push_higher(net_dev, lp, ql->skb, proto);
- qn = ql->next;
- kfree(ql);
- ql = qn;
- }
- q = net_dev->ib.sq;
- } else
- q = q->next;
- }
- net_dev = net_dev->next;
- }
+ /* gone */
#endif
}

--- linux-2.2.14.mp/drivers/isdn/isdn_net.c.orig Fri Mar 10 18:31:10
2000
+++ linux-2.2.14.mp/drivers/isdn/isdn_net.c Fri Mar 10 18:31:10 2000
@@ -2640,11 +2640,6 @@
}
netdev->local->magic = ISDN_NET_MAGIC;

-#ifdef CONFIG_ISDN_PPP
- netdev->mp_last = NULL; /* mpqueue is empty */
- netdev->ib.next_num = 0;
- netdev->ib.last = NULL;
-#endif
netdev->queue = netdev->local;
netdev->local->last = netdev->local;
netdev->local->netdev = netdev;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:57    [from the cache]
©2003-2011 Jasper Spaans