/* atmrecv.c */ /* Low level receive frame drivers and buffer managers */ /* Copyright (c) 1996 Robert Geist and James Westall * Clemson University Dept. of Computer Science * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "atmdd.h" #include "atmxram.h" #ifdef FULL_STACK #include #include #include #include #endif #include static struct rbhtype *lastused; /* These values are used to track available buffer headers */ /* and thus indirectly the loss of SKB's. Normally this */ /* driver allocates a pool of SKB's and accepts return of */ /* them from the protocol stack... But when a process */ /* terminates abnormally, unread skbs can apparently be */ /* kfreed.. This occurrence causes the free_rbh count to */ /* build up. */ #define LOST_SKB_THRESHOLD (SKB_COUNT / 4) int b_freerbh = 0; int c_freerbh = 0; int in_recvint = 0; /**/ /* Free a receive buffer header */ void atm_freerbh( struct pddtype *ape, struct rbhtype *rbh) { ape->efreerbh->next = rbh; ape->efreerbh = rbh; rbh->next = (struct rbhtype *)1; atomic_inc((atomic_t *)&c_freerbh); } /**/ /* Allocate a new receive buffer header */ struct rbhtype *atm_getrbh( struct pddtype *ape) { struct rbhtype *rbh; rbh = ape->sfreerbh; if (rbh->next == (struct rbhtype *)1) return(NULL); ape->sfreerbh = rbh->next; rbh->next = (struct rbhtype *)1; atomic_dec((atomic_t *)&c_freerbh); return(rbh); } #ifdef FULL_STACK /**/ /* See if free list is full */ int atm_needskbs( struct pddtype *ape) { /* If current free buffer header count is < original free */ /* buffer header count then we actually have more skbs */ /* than we started with */ if (c_freerbh < b_freerbh) return(0); return(1); } /**/ /* Replenish free buffer list */ int atm_addskbs( struct pddtype *ape) { struct rbhtype *rbh; struct sk_buff *skb; if ((c_freerbh - b_freerbh) <= LOST_SKB_THRESHOLD) { #if 0 printk("atm_addskbs: Not getting %d new skb's - current: %d base: %d \n", c_freerbh - b_freerbh, c_freerbh, b_freerbh); #endif return(0); } #ifdef DEBUG_RECV printk("atm_addskbs: Getting %d new skb's - current: %d base: %d \n", c_freerbh - b_freerbh, c_freerbh, b_freerbh); #endif while (c_freerbh > b_freerbh) { rbh = (struct rbhtype *)atm_getrbh(ape); if (rbh == NULL) { printk("atm_addskbs: YEOW.. No free rbhs \n"); return(0); } skb = alloc_skb(RB_SIZE, GFP_ATOMIC); if (skb == NULL) { atm_freerbh(ape, rbh); printk("atm_addskbs: No free skbs \n"); return(0); } skb->len = RB_SIZE; rbh->vdata = (char *)skb->data; rbh->data = (char *)virt_to_phys(skb->data); rbh->len = RB_SIZE; rbh->skb = skb; ape->erfl->next = (struct rbhtype *)virt_to_phys(rbh); ape->erfl = rbh; } return(1); } #endif /**/ /* Initialize receive buffer management structures */ int atm_initrb( struct pddtype *ape) { struct rbhtype *rbh; struct rbhtype *nrbh; struct rbftype *rbf; unsigned long loc; unsigned int regid; int i; #ifdef FULL_STACK struct sk_buff *skb; #endif /* Begin by putting the 1st rbh on the free rbh list */ rbh = ape->rbpool->rbhs; ape->sfreerbh = rbh; ape->efreerbh = rbh; /* Now add the rest of them */ for (i = 1; i < RBH_COUNT; i++) { memset(rbh, 0, sizeof(struct rbhtype)); rbh->data = NULL; rbh->len = RB_SIZE; atm_freerbh(ape, rbh); rbh += 1; } /* Set up the dummy descriptors for the 8 input queues. */ regid = RRL0_LBDA_HI; for (i = 0; i < 8; i++) { rbh = atm_getrbh(ape); rbh->next = (struct rbhtype *)1; ape->srrl[i] = rbh; loc = (unsigned long)virt_to_phys(rbh); WT_CNTLREG(ape, regid + 2, loc & 0xffff); WT_CNTLREG(ape, regid, (loc >> 16) & 0xffff); regid += 4; } #ifdef DEBUG_RECV printk("Rdy List header for vc 33 at %08x \n", ape->slcrbl + 33); printk("Rdy List trailr for vc 33 at %08x \n", ape->elcrbl + 33); printk("Rdy List header for vc 34 at %08x \n", ape->slcrbl + 34); printk("Rdy List trailr for vc 33 at %08x \n", ape->elcrbl + 34); #endif #ifdef FULL_STACK /* Bind a real buffer to a header and put it on the free */ /* buffer list. */ rbh = atm_getrbh(ape); skb = alloc_skb(RB_SIZE, GFP_KERNEL); if (skb == 0) { printk("atm_initrb: YEOW... no skbuff available! \n"); return(-1); } skb->len = RB_SIZE; rbh->vdata = (char *)skb->data; rbh->data = (char *)virt_to_phys(rbh->vdata); rbh->len = RB_SIZE; rbh->skb = skb; loc = (unsigned long)virt_to_phys(rbh); #ifdef DEBUG_RECV printk("atm_initrb: SRFL at %x \n", loc); #endif WT_CNTLREG(ape, SRFL_LO, loc & 0xffff); WT_CNTLREG(ape, SRFL_HI, (loc >> 16) & 0xffff); /* Bind remaining real buffers to headers and put them */ /* on the free list. */ for (i = 1; i < SKB_COUNT; i++) { nrbh = atm_getrbh(ape); skb = alloc_skb(RB_SIZE, GFP_KERNEL); if (skb == 0) { printk("atm_initrb: YEOW... no skbuff available! \n"); return(-1); } skb->len = RB_SIZE; nrbh->vdata = (char *)skb->data; nrbh->data = (char *)virt_to_phys(nrbh->vdata); nrbh->len = RB_SIZE; nrbh->skb = skb; rbh->next = (struct rbhtype *)virt_to_phys(nrbh); rbh = nrbh; rbf += 1; } /* Remember the end of the free buffer list */ rbh->next = (struct rbhtype *)1; ape->erfl = rbh; /* Remember the base rbh count */ b_freerbh = c_freerbh; return(0); #else /* Standalone driver code */ /* Now set up the LC based input queues */ for (i = 0; i < LC_COUNT; i++) { rbh = atm_getrbh(ape); rbh->next = (struct rbhtype *)1; ape->slcrbl[i] = rbh; ape->elcrbl[i] = rbh; init_waitqueue_head(&ape->rlc_waitq[i]); } /* Bind a real buffer to a header and put it on the free */ /* buffer list. */ rbh = atm_getrbh(ape); rbf = ape->rbpool->rbfs; printk("Read buffer headers at %08x \n", ape->rbpool->rbhs); printk("Read buffer pool at %08x \n", ape->rbpool->rbfs); rbh->vdata = (char *)rbf; rbh->data = (char *)virt_to_phys((char *)rbf); rbh->len = RB_SIZE; loc = (unsigned long)virt_to_phys(rbh); WT_CNTLREG(ape, SRFL_LO, loc & 0xffff); WT_CNTLREG(ape, SRFL_HI, (loc >> 16) & 0xffff); /* Bind remaining real buffers to headers and put them */ /* on the free list. */ rbf += 1; for (i = 1; i < RB_COUNT; i++) { nrbh = atm_getrbh(ape); nrbh->vdata = (char *)rbf; nrbh->data = (char *) virt_to_phys(rbf); nrbh->next = (struct rbhtype *)1; nrbh->len = RB_SIZE; rbh->next = (struct rbhtype *)virt_to_phys(nrbh); rbh = nrbh; rbf += 1; } /* Remember the end of the free buffer list */ ape->erfl = rbh; return(0); #endif } /**/ /* Queue managment for discarding input sent to unassigned */ /* queues.. This serves to digest unsolicited OAM packets. */ #ifndef FULL_STACK void atm_recvdump( struct pddtype *ape, unsigned int qid, /* Id of queue needing service */ unsigned short sisr) { struct rbhtype *rbh; struct rbhtype *lrbh; int rbnx; /* Recover the existing pointer to the dummy element at */ /* the start of this ready queue.. It should NOT have */ /* a next pointer of 1... because that means we got a */ /* receive ready interrupt with nothing added to the */ /* queue. */ rbh = ape->srrl[qid]; if (rbh->next == (struct rbhtype *)1) { #if 1 printk("atm_recvdump: Called with empty list \n"); printk("atm_recvdump: sisr was %04x \n", sisr); printk("atm_recvdump: Queue id is %d \n", qid); printk("atm_recvdump: last rbh at %08x \n", lastused); printk("atm_recvdump: rbh sdulen was %08x \n", rbh->sdulen); printk("atm_recvdump: rbh len was %08x \n", rbh->len); printk("atm_recvdump: rbh lci was %08x \n", rbh->lci); printk("atm_recvdump: rbh statusi was %08x \n", rbh->status); #endif return; } /* Go through the list of ready descriptors consuming */ /* the data. */ do { lrbh = rbh; if (rbh->next != (struct rbhtype *)1) rbh->next = (struct rbhtype *)phys_to_virt((int)rbh->next); rbh = rbh->next; #if 1 printk("atm_rcvdump: Freed buffer at %08x \n", rbh); #endif } while (rbh->next != (struct rbhtype *)1); /* Finally copy the consumed descriptors to the free list */ /* We do a trick here of rebinding the data buffer that */ /* was associated with the last descriptor back to the */ /* first descriptor because we must leave the last desc */ /* on the list as a dummy AND the first descriptor starts */ /* with a NULL data pointer. */ ape->srrl[qid]->vdata = rbh->vdata; printk("atm_rcvdump: Rebinding \n"); lrbh->next = (struct rbhtype *)1; /* Set end of free list */ ape->erfl->next = ape->srrl[qid]; /* Attach to free list */ ape->srrl[qid] = rbh; /* Update ready list start */ ape->erfl = lrbh; /* Update free list end */ } #endif #ifdef FULL_STACK /**/ /* Receive buffer management */ void atm_recvbuf( struct pddtype *ape, struct rbhtype *rbh) { #ifdef FULL_STACK struct atm_vcc *vcc; #endif int lcid; int lcndx; int len; int rbndx; /* Check for good status in the RBH */ if ((rbh->status & 0xff) != 3) { if ((rbh->status & 0xff) == 1) printk("atm_recvbuf: YEOW scatter/gather not supported \n"); printk("atm_recvbuf: Status %x in rbh %d \n", rbh->status, rbh - ape->rbpool->rbhs); if (rbh->skb != 0) dev_kfree_skb(rbh->skb); return; } /* The LC id is actually the relative location of the LC */ /* data structure in XRAM divided by 128.. We layout all */ /* LC structures in order. So to avoid wasting table */ /* space we convert to a relative LC number for buffer */ /* queue managemement functions. */ lcid = rbh->lci; len = rbh->sdulen; lcndx = lcid - FIRST_LCID; #ifdef DEBUG_RECV rbndx = rbh - ape->rbpool->rbhs; printk("atm_recvdq: Next rbh index is %d \n", rbndx); printk("atm_recvdq: Got %d bytes for lc %x \n", len, lcid); #endif /* If this buffer is associated with a legit connection */ /* then push it to the protocol layer. */ ape25_getvcc(lcndx, &vcc); if (vcc != 0) { #ifdef DEBUG_RECV printk("atm_recvdq: Pushing skbuffer at %08x \n", rbh->skb); printk("atm_recvdq: Pushing to vci %d \n", vcc->vci); #endif if (rbh->skb) { atm_charge(vcc, rbh->skb->truesize); #ifdef CAPTURE_PMD ape->pmddata->lcin[ape->lc_to_vci[lcndx]] += 1; #endif /* rbh->skb->atm.vcc = vcc; */ /* Patch courtesy of */ ATM_SKB(rbh->skb)->vcc = vcc; /* Heikki Vatianinen */ rbh->skb->len = len; /* printk("atm_recvdq: rx_inuse is %d on %d \n", vcc->rx_inuse, vcc->vci); */ vcc->push(vcc, rbh->skb); } else { printk("atm_recvdq: YEOW .. null skb pointer! \n"); } } else /* Received a packet for an inactive LC */ { printk("atm_recvbuf: Dumping packet for null vcc \n"); if (rbh->skb != 0) dev_kfree_skb(rbh->skb); } } #endif /**/ /* Queue managment for receive processing */ void atm_recvdq( struct pddtype *ape, unsigned int qid, /* Id of queue needing service */ unsigned short sisr) { #ifdef FULL_STACK struct atm_vcc *vcc; #endif struct rbhtype *rbh; struct rbhtype *nrbh; struct rbhtype *firstrbh; int lcid; int lcndx; int len; int rbndx; int rc; /* Recover the existing pointer to the dummy element at */ /* the start of this ready queue.. It should NOT have */ /* a next pointer of 1... because that means we got a */ /* receive ready interrupt with nothing added to the */ /* queue. */ rbh = ape->srrl[qid]; firstrbh = rbh; #ifdef DEBUG_RECV rbndx = rbh - ape->rbpool->rbhs; printk("atm_recvdq: First rbh index is %d \n", rbndx); #endif if (rbh->next == (struct rbhtype *)1) { #ifdef DEBUG_RECV /* This stuff here is actually depressingly */ /* NORMAL under heavy loads.. Manual sez */ /* driver must defend against it. */ printk("atm_recvdq: Called with empty list \n"); printk("atm_recvdq: Queue id is %d \n", qid); printk("atm_recvdq: sisr was %04x \n", sisr); printk("atm_recvdq: rbh was at %08x \n", rbh); printk("atm_recvdq: last rbh at %08x \n", lastused); printk("atm_recvdq: rbh sdulen was %08x \n", rbh->sdulen); printk("atm_recvdq: rbh len was %08x \n", rbh->len); printk("atm_recvdq: rbh lci was %08x \n", rbh->lci); printk("atm_recvdq: rbh statusi was %08x \n", rbh->status); #endif return; } #ifdef FULL_STACK /* Go through the list of ready descriptors consuming */ /* the data. Each buffer is pushed to a queue */ /* associated with the LC of the data. */ rbh->next = (struct rbhtype *)phys_to_virt((int)rbh->next); nrbh = rbh->next; atm_freerbh(ape, rbh); /* Free the dummy rbh. */ rbh = nrbh; while (rbh->next != (struct rbhtype *)1) { #ifdef DEBUG_RECV printk("atm_recdq: Processed buffer at %08x \n", rbh); #endif if (rbh->next != (struct rbhtype *)1) rbh->next = (struct rbhtype *)phys_to_virt((int)rbh->next); nrbh = rbh->next; atm_recvbuf(ape, rbh); /* Process this buffer */ rbh->skb = 0; atm_freerbh(ape, rbh); rbh = nrbh; } /* Now process the last rbh in the queue list ... We can't */ /* free the rbh but we do push the associated skbuff */ atm_recvbuf(ape, rbh); /* Process this buffer */ /* Update the new start of the receive ready list */ ape->srrl[qid] = rbh; /* Update ready list start */ #else /* Standalone driver code */ /* Go through the list of ready descriptors consuming */ /* the data. Each descriptor is moved a queue */ /* associated with the LC of the data. */ if (rbh->next != (struct rbhtype *)1) rbh->next = (struct rbhtype *) phys_to_virt((int)rbh->next); rbh = rbh->next; #ifdef DEBUG_RECV rbndx = rbh - ape->rbpool->rbhs; printk("atm_recvdq: Second rbh index is %d \n", rbndx); #endif while (rbh->next != (struct rbhtype *)1) { #ifdef DEBUG_RECV printk("atm_recdq: Processed buffer at %08x \n", rbh); #endif /* The LC id is actually the relative location of the LC */ /* data structure in XRAM divided by 128.. We layout all */ /* LC structures in order. So to avoid wasting table */ /* space we convert to a relative LC number for buffer */ /* queue managemement functions. */ if (rbh->next != (struct rbhtype *)1) rbh->next = phys_to_virt((int)rbh->next); nrbh = rbh->next; #ifdef 1 rbndx = nrbh - ape->rbpool->rbhs; printk("atm_recvdq: Next rbh index is %d \n", rbndx); #endif lcid = rbh->lci; len = rbh->sdulen; lcndx = lcid - FIRST_LCID; #ifdef DEBUG_RECV printk("Got %d bytes for lc %x \n", len, lcid); printk("atm_recvdata: Old last buffer at %08x \n", ape->elcrbl[lcndx]); printk("atm_recvdata: New last buffer at %08x \n", rbh); #endif /* Append the buffer to the target LC's input queue */ ape->elcrbl[lcndx]->next = rbh; ape->elcrbl[lcndx] = rbh; rbh->next = (struct rbhtype *)1; /* Update current buffer header pointer */ rbh = nrbh; } /* We can't mess around with the last rbh in the list */ /* lest we encounter mutex problems with the APE - 25 */ /* But we do want to consume its data... Thus we copy */ /* it to the dummy one that was at the head of the */ /* list. */ memcpy(firstrbh, rbh, sizeof(struct rbhtype)); lastused = rbh; #ifdef DEBUG_RECV printk("atm_recdq: Last buffer at %08x \n", rbh); #endif #ifdef DEBUG_RECV rbndx = rbh - ape->rbpool->rbhs; printk("atm_recvdq: Last rbh index is %d \n", rbndx); #endif lcid = rbh->lci; #if 0 if (lcid != 0x47) { printk("Yeow... bad lcid %d \n", lcid); printk("Yeow... buflen %d \n", rbh->sdulen); ape->srrl[qid] = rbh; /* Update ready list start */ return; } #endif len = rbh->sdulen; lcndx = lcid - FIRST_LCID; ape->elcrbl[lcndx]->next = firstrbh; ape->elcrbl[lcndx] = firstrbh; firstrbh->next = (struct rbhtype *)1; /* Update the new start of the receive ready list */ ape->srrl[qid] = rbh; /* Update ready list start */ /* Wake up any processes that may be sleeping on this LC */ wake_up_interruptible(&ape->rlc_waitq[lcndx]); #endif } /**/ /* Receive interrupt processing. */ void atm_recvint( struct pddtype *ape, unsigned short sisr) /* Int status reg at time of int */ { int i; unsigned short mask; int regid; in_recvint = 1; mask = 0x8000; for (i = 0; i < 8; i++) { if (sisr & mask) { if (i == 0) atm_recvdq(ape, i, sisr); else if (i == 1) atm_recvdq(ape, i, sisr); else { printk("atm_recvint: oddball recv int on q %d \n", i); atm_recvdq(ape, i, sisr); } /* atm_recvdump(ape, i, sisr); */ } mask >>= 1; } #ifdef FULL_STACK if ((c_freerbh - b_freerbh) > LOST_SKB_THRESHOLD) atm_addskbs(ape); #endif in_recvint = 0; } #ifndef FULL_STACK /**/ /* User level receive processing */ int atm_recv5u( struct pddtype *ape, int vcid, /* logical channel id. */ char *message, /* Pointer to data. */ int *len) /* Length of message */ { int lcndx; struct rbhtype *rbh; struct rbhtype *firstrbh; int count; int lcid; unsigned long flags; unsigned long lockflags; spin_lock_irqsave(&ape->recvlock, lockflags); #if 0 lcid = atm_testvci(ape, vcid); if (lcid < 0) return(lcid); #endif lcndx = vcid; lcid = lcndx + FIRST_LCID; #ifdef DEBUG_RECV printk("atm_recv5u: receive request for queue %x \n", lcndx); #endif firstrbh = ape->slcrbl[lcndx]; count = 0; while (firstrbh->next == (struct rbhtype *)1) { #ifdef DEBUG_RECV printk("atm_recv5u: dummy at %x next at %x \n", firstrbh, firstrbh->next); printk("atm_recv5u: no data sleeping on queue %x \n", lcndx); printk("atm_recv5u: queue ptr is %x \n", ape->rlc_waitq[lcndx]); printk("atm_recv5u: before unlock val is %x \n", ape->recvlock); #endif spin_unlock_irqrestore(&ape->recvlock, lockflags); /* printk("atm_recv5u: after unlock val is %x \n", ape->recvlock); */ interruptible_sleep_on(&ape->rlc_waitq[lcndx]); /* printk("atm_recv5u: after wakeup val is %x \n", ape->recvlock); */ if (signal_pending(current)) { printk("atm_recv5u: Returning on sigint \n"); return(-10); } /* printk("atm_recv5u: after wakeup val is %x \n", ape->recvlock); */ spin_lock_irqsave(&ape->recvlock, lockflags); /* printk("atm_recv5u: after relock val is %x \n", ape->recvlock); */ count += 1; if (count > 255) { *len = -1; return(-1); } firstrbh = ape->slcrbl[lcndx]; } rbh = firstrbh->next; #ifdef DEBUG_RECV printk("atm_recv5u: dummy at %x data at %x \n", firstrbh, rbh); printk("atm_recv5u: user buffer at %x \n", message); printk("atm_recv5u: data buffer at %x \n", rbh->data); #endif /* Copy the data back to the user buffer */ copy_to_user(message, rbh->vdata, rbh->sdulen); *len = rbh->sdulen; /* Rebind the consumed data buffer to the dummy buffer header */ /* so that it can be put back on the free buffer list. */ memcpy(firstrbh, rbh, sizeof(struct rbhtype)); firstrbh->next = (struct rbhtype *)1; ape->erfl->next = (struct rbhtype *)virt_to_phys(firstrbh); ape->erfl = firstrbh; /* Update the start of the LC buffer list to point */ /* to this - now dummy - header. */ #ifdef DEBUG_RECV printk("atm_recv5u: new dummy at %x \n", rbh); #endif ape->slcrbl[lcndx] = rbh; /* printk("atm_recv5u: before final unlock is %x \n", ape->recvlock); */ spin_unlock_irqrestore(&ape->recvlock, lockflags); } #endif /**/ /* Validate buffer queues.. or show them invalid */ int atm_vbq( struct pddtype *ape) { unsigned long loc; struct rbhtype *rbh; struct rbftype *rbf; int rbhndx; int rbfndx; int count = 0; /* Print the receive free list */ loc = RD_CNTLREG(ape, SRFL_HI); loc <<= 16; loc |= RD_CNTLREG(ape, SRFL_LO); rbh = (struct rbhtype *)phys_to_virt(loc); printk("Receive free list \n"); do { rbhndx = rbh - ape->rbpool->rbhs; count += 1; printk("%4d \n", rbhndx); if (rbh->next != (struct rbhtype *)1) rbh = (struct rbhtype *)phys_to_virt((int)rbh->next); else rbh = (struct rbhtype *)1; } while (rbh != (struct rbhtype *)1); printk("Total free buffers = %d \n", count); printk("Free RBH count = %d \n", c_freerbh); rbh = ape->sfreerbh; count = 0; printk("Receive free rbh list \n"); do { rbhndx = rbh - ape->rbpool->rbhs; count += 1; printk("%4d \n", rbhndx); rbh = rbh->next; } while (rbh != (struct rbhtype *)1); printk("Total free RBHs = %d \n", count); printk("Free RBH count = %d \n", c_freerbh); }