📄 pfilt.c
字号:
#endif#ifdef SELF_PROF if (enSelfProf) { struct timeval starttv, endtv; microtime(&starttv); microtime(&endtv); enCalibrateProf.tv_sec += (endtv.tv_sec - starttv.tv_sec); enCalibrateProf.tv_usec += (endtv.tv_usec - starttv.tv_usec); enCalibrateCount++; }#endif SELF_PROF#ifdef GNODE_CLONING /* * Each open enet file has a different minor device number. * When a user tries to open any of them, we actually open * any available minor device and associate it with the * corresponding unit. * * This is not elegant, but UNIX will call * open for each new open file using the same inode but calls * close only when the last open file referring to the inode * is released. This means that we cannot know inside the * driver code when the resources associated with a particular * open of the same inode should be deallocated. Thus, we have * to make up a temporary inode to represent each simultaneous * open of the ethernet. Each inode has a different minor device number. */ unit = minor(dev); /* check for illegal unit */ if ( (unit >= enUnits) /* bad unit */ || (enet_info[unit].ifp == 0) /* ifp not known */ || ((enet_info[unit].ifp->if_flags & IFF_UP) == 0) ) /* or if down */ { return(ENXIO); }#else /* * Without gnode cloning, we have to have a separate entry in * /dev/ for each minor device. They all start out bound to * the first "up" interface. */ for (unit = 0; unit < enUnits; unit++) { /* check all units in order */ if (enet_info[unit].ifp /* interface known */ && (enet_info[unit].ifp->if_flags & IFF_UP) ) /* and up */ break; /* fall out of loop */ } /* check for illegal unit (i.e., nothing "up") */ if (unit >= enUnits) /* bad unit */ return(ENXIO);#endif GNODE_CLONING#ifdef GNODE_CLONING /* Allocate a minor device number */ md = PfiltFindMinor();#ifdef DEBUG Pfiltprintf(ENDBG_TRACE)("Pfilt_open: md = %d\n", md);#endif if (md < 0) { return(EBUSY); } *newmin = md;#else /* Use the minor device number supplied */ md = minor(dev); if (md >= NPACKETFILTER) return(ENXIO); /* an illegal minor device number */ if (enAllocMap[md]) return(EBUSY); /* already in use */#endif GNODE_CLONING enUnitMap[md] = unit; enAllocMap[md] = TRUE; enStatep = enStatePs[unit]; Pfiltprintf(ENDBG_DESQ) ("Pfilt_open: Desq: %x, %x\n", enDesq.enQ_F, enDesq.enQ_B); s = splenet(); /* SMP */ smp_lock(&lk_pfilt, LK_RETRY); pfactive++; /* Allocate memory for this descriptor */ KM_ALLOC(enAllDescriptors[md], struct enOpenDescriptor *, sizeof(struct enOpenDescriptor), KM_PFILT, KM_NOW_CL); if (enAllDescriptors[md] == NULL) return(ENOBUFS); PfiltInitDescriptor(enAllDescriptors[md], ENHOLDSIG); /* ENHOLDSIG is set by default; historical accident */ PfiltInsertDescriptor(&(enDesq), enAllDescriptors[md]); smp_unlock(&lk_pfilt); splx(s); return(0);}/* * PfiltFindMinor - find a free logical device on specified unit */PfiltFindMinor(){ register int md; for (md = 0; md < enMaxMinors; md++) { if (enAllocMap[md] == FALSE) return(md); } return(-1);}/* * PfiltInit - initialize ethernet unit (called by pfilt_attach) */PfiltInit(enStatep, unit)register struct enState *enStatep;register int unit;{ int s;#ifdef DEBUG Pfiltprintf(ENDBG_INIT)("PfiltInit(%x %d):\n", enStatep, unit);#endif s = splenet(); /* SMP */ smp_lock(&lk_pfilt, LK_RETRY); /* initialize free queue if not already done */ if (enFreeq.enQ_F == 0) { register int i; pfiltinitqueue((struct Queue *)&enFreeq); for (i=0; i<ENPACKETS; i++) { register struct enPacket *p; p = &enQueueElts[i]; p->enP_RefCount = 0; p->enP_Stamp.ens_stamplen = sizeof(struct enstamp); PfiltDeallocatePacket(p); } /* calculate scavenger thresholds */ PfiltInitScavenge(); /* also a good time to init enAllocMap */ for (i = 0; i < enMaxMinors; i++) enAllocMap[i] = FALSE; } pfiltinitqueue((struct Queue *)&enDesq); /* init descriptor queue */ enStatep->ens_UserMaxWaiting = ENNOTSUWAITING; enStatep->ens_AllowPromisc = false; smp_unlock(&lk_pfilt); splx(s);}/* * Pfilt_close - ether net device close routine *//* ARGSUSED */Pfilt_close(dev, flag){ register int md = ENINDEX(dev); register struct enState *enStatep = enStatePs[ENUNIT(dev)]; register struct enOpenDescriptor *d = enAllDescriptors[md]; struct enPacket *dummy; /* a dummy pointer - used only for consistency * in the macro pfiltdequeue */ int ipl; extern PfiltTimeout(); enAllocMap[md] = FALSE;#ifdef DEBUG Pfiltprintf(ENDBG_TRACE)("Pfilt_close(%d, %x):\n", md, flag);#endif /* * insure that receiver doesn't try to queue something * for the device as we are decommissioning it. * (I don't think this is necessary, but I'm a coward.) */ ipl = splenet(); smp_lock(&lk_pfilt, LK_RETRY); pfactive--; /* if necessary, drop counter for promiscuous mode */ if (d->enOD_Flag & ENPROMISC) { decPromiscCount(enStatep, dev); } /* if necessary, drop counter for copy-all mode */ if (d->enOD_Flag & ENCOPYALL) { decCopyAllCount(enStatep, dev); } pfiltdequeue((struct Queue *)d->enOD_Link.B, dummy); enCurOpens--; Pfiltprintf(ENDBG_DESQ) ("Pfilt_close: Desq: %x, %x\n", enDesq.enQ_F, enDesq.enQ_B); PfiltFlushWaitQueue(d); untimeout(PfiltTimeout, (caddr_t)d); KM_FREE(d, KM_PFILT); enAllDescriptors[md] = (struct enOpenDescriptor *)0; smp_unlock(&lk_pfilt); splx(ipl);}/* * Pfilt_read - read next packet from net *//* VARARGS */Pfilt_read(dev, uio)dev_t dev;register struct uio *uio;{ register struct enOpenDescriptor *d = enAllDescriptors[ENINDEX(dev)]; register struct enPacket *p; int ipl; int error; int padneeded = 0; /* modified by Pfiltrmove() */ int first = 1; /* is this the first packet in a batch? */ extern PfiltTimeout();#if DEBUG Pfiltprintf(ENDBG_TRACE)("Pfilt_read(%x):", dev);#endif ipl = splenet(); smp_lock(&lk_pfilt, LK_RETRY); /* * If nothing is on the queue of packets waiting for * this open enet file, then set timer and sleep until * either the timeout has occurred or a packet has * arrived. */ while (0 == d->enOD_Waiting.enWQ_NumQueued) { if (d->enOD_Timeout < 0) { smp_unlock(&lk_pfilt); splx(ipl); return(0); } if (d->enOD_Timeout) { /* * If there was a previous timeout pending for this file, * cancel it before setting another. This is necessary since * a cancel after the sleep might never happen if the read is * interrupted by a signal. */ if (d->enOD_RecvState == ENRECVTIMING) untimeout(PfiltTimeout, (caddr_t)d); timeout(PfiltTimeout, (caddr_t)d, (int)(d->enOD_Timeout)); d->enOD_RecvState = ENRECVTIMING; } else d->enOD_RecvState = ENRECVIDLE; sleep_unlock((caddr_t)d, PRINET, &lk_pfilt); smp_lock(&lk_pfilt, LK_RETRY); switch (d->enOD_RecvState) { case ENRECVTIMING: { untimeout(PfiltTimeout, (caddr_t)d); d->enOD_RecvState = ENRECVIDLE; break; } case ENRECVTIMEDOUT: { smp_unlock(&lk_pfilt); splx(ipl); return(0); } } } /* We believe there is something waiting for us in the queue */ while (1) { if (d->enOD_Waiting.enWQ_NumQueued <= 0) { /* * Either we emptied the queue or someone else did while * we weren't looking. */ smp_unlock(&lk_pfilt); splx(ipl); return(0); } p = PfiltDeWaitQueue(&(d->enOD_Waiting)); if (first) { first = 0; } else { /* this is not the first packet in a batch */ if (uio->uio_resid < (padneeded + sizeof(struct enstamp) + p->enP_ByteCount)) { /* no more room in user's buffer; don't truncate packet */ PfiltPutBack(&(d->enOD_Waiting), p); smp_unlock(&lk_pfilt); splx(ipl); return(0); } } /* * Move data from packet into user space. */ smp_unlock(&lk_pfilt); /* unlock for uiomove */ splx(ipl); error = Pfiltrmove(p, uio, d->enOD_Flag, &padneeded, d->enOD_Truncation); ipl = splenet(); smp_lock(&lk_pfilt, LK_RETRY); /* relock after uiomove */ if (--(p->enP_RefCount) <= 0) /* if no more claims on this packet */ { m_freem(p->enP_mbuf); /* release mbuf */ PfiltDeallocatePacket(p); /* and packet */ } if (d->enOD_Flag & ENBATCH && !error && uio->uio_resid > 0) continue; else break; } smp_unlock(&lk_pfilt); splx(ipl); return(error);}/* * PfiltTimeout - process ethernet read timeout */PfiltTimeout(d)register struct enOpenDescriptor * d;{ register int ipl; register struct enOpenDescriptor *t; register struct enState *enStatep; register int unit;#ifdef DEBUG Pfiltprintf(ENDBG_TRACE)("PfiltTimeout(%x):\n", d);#endif ipl = splenet(); smp_lock(&lk_pfilt, LK_RETRY); if (smp) { /* don't call Pfilt_wakeup if the descriptor has gone away */ for (unit = 0; unit < enUnits; unit++) { enStatep = enStatePs[unit]; if (enDesq.enQ_F == 0) continue; /* never initialized */ forAllOpenDescriptors(t) { if (d == t) goto out; } } /* if we fall through to here, then descriptor is no longer valid */ smp_unlock(&lk_pfilt); splx(ipl); return(0); }out: d->enOD_RecvState = ENRECVTIMEDOUT; Pfilt_wakeup(d); smp_unlock(&lk_pfilt); splx(ipl); wakeup((caddr_t)d);}/* * Pfilt_write - write next packet to net */int PfiltKludgeSleep[MAXUNITS]; /* Are we sleeping on IF_QFULL? */ /* really, # of procs sleeping on IF_QFULL *//* VARARGS */Pfilt_write(dev, uio)dev_t dev;register struct uio *uio;{ register int unit = ENUNIT(dev); register struct enState *enStatep = enStatePs[unit]; struct mbuf *mp; register struct ifnet *ifp = enet_info[unit].ifp; int ipl; int error; int sleepcount; int PfiltKludgeTime();#if DEBUG Pfiltprintf(ENDBG_TRACE)("Pfilt_write(%x):\n", dev);#endif if (uio->uio_resid == 0) return(0); if (uio->uio_resid > ifp->if_mtu) /* too large */ return(EMSGSIZE); /* * Copy user data into mbufs */ if (error = Pfiltwmove(uio, &mp)) { return(error); } ipl = splenet(); smp_lock(&lk_pfilt, LK_RETRY); /* * if the queue is full, * hang around until there's room or until process is interrupted */ sleepcount = 0; while (IF_QFULL(&(ifp->if_snd))) { if (sleepcount++ > 2) { /* don't sleep too long */ smp_unlock(&lk_pfilt); splx(ipl); return(ETIMEDOUT); } /* if nobody else has a timeout pending for this unit, set one */ if (PfiltKludgeSleep[unit] == 0) timeout(PfiltKludgeTime, (caddr_t)unit, 2 * hz); PfiltKludgeSleep[unit]++; /* record that we are sleeping */ if (setjmp(&u.u_qsave)) { /* sleep (following) was interrupted, clean up */#if DEBUG Pfiltprintf(ENDBG_MISC) ("Pfilt_write(%x): enet%d sleep %d interrupted\n", dev, unit, PfiltKludgeSleep[unit]);#endif DEBUG PfiltKludgeSleep[unit]--; /* we're no longer sleeping */ m_freem(mp); splx(ipl); return(EINTR); } sleep_unlock((caddr_t)&(PfiltKludgeSleep[unit]), PRINET, &lk_pfilt); smp_lock(&lk_pfilt, LK_RETRY); PfiltKludgeSleep[unit]--; /* we are no longer sleeping */ } smp_unlock(&lk_pfilt); /* place mbuf chain on outgoing queue & start if necessary */ error = (*ifp->if_output)(ifp, mp, &enetaf); /* this always frees the mbuf chain */ enXcnt++; splx(ipl); return(error);}PfiltKludgeTime(unit)int unit;{ /* XXX perhaps we should always wakeup? */ if (PfiltKludgeSleep[unit]) { wakeup((caddr_t)&(PfiltKludgeSleep[unit])); /* XXX should we restart transmitter? */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -