f12744b5db
to prevent deadlock on media unbind (which is called with the interface wlock()'ed), the medias reader processes that unbind was waiting for used to discard packets when the interface could not be rlocked. this has the unfortunate side effect that when we change addresses on a interface that packets are getting lost. this is problematic for the processing of ipv6 router advertisements when multiple RA's are getting received in quick succession. this change removes that packet dropping behaviour and instead changes the unbind process to avoid the deadlock by wunlock()ing the interface temporarily while waiting for the reader processes to finish. the interface media is also changed to the mullmedium before unlocking (see the comment).
159 lines
2.6 KiB
C
159 lines
2.6 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
|
|
#include "ip.h"
|
|
|
|
static void netdevbind(Ipifc *ifc, int argc, char **argv);
|
|
static void netdevunbind(Ipifc *ifc);
|
|
static void netdevbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
|
|
static void netdevread(void *a);
|
|
|
|
typedef struct Netdevrock Netdevrock;
|
|
struct Netdevrock
|
|
{
|
|
Fs *f; /* file system we belong to */
|
|
Proc *readp; /* reading process */
|
|
Chan *mchan; /* Data channel */
|
|
};
|
|
|
|
Medium netdevmedium =
|
|
{
|
|
.name= "netdev",
|
|
.hsize= 0,
|
|
.mintu= 0,
|
|
.maxtu= 64000,
|
|
.maclen= 0,
|
|
.bind= netdevbind,
|
|
.unbind= netdevunbind,
|
|
.bwrite= netdevbwrite,
|
|
.unbindonclose= 0,
|
|
};
|
|
|
|
/*
|
|
* called to bind an IP ifc to a generic network device
|
|
* called with ifc qlock'd
|
|
*/
|
|
static void
|
|
netdevbind(Ipifc *ifc, int argc, char **argv)
|
|
{
|
|
Chan *mchan;
|
|
Netdevrock *er;
|
|
|
|
if(argc < 2)
|
|
error(Ebadarg);
|
|
|
|
mchan = namec(argv[2], Aopen, ORDWR, 0);
|
|
|
|
er = smalloc(sizeof(*er));
|
|
er->readp = (void*)-1;
|
|
er->mchan = mchan;
|
|
er->f = ifc->conv->p->f;
|
|
|
|
ifc->arg = er;
|
|
|
|
kproc("netdevread", netdevread, ifc);
|
|
}
|
|
|
|
/*
|
|
* called with ifc wlock'd
|
|
*/
|
|
static void
|
|
netdevunbind(Ipifc *ifc)
|
|
{
|
|
Netdevrock *er = ifc->arg;
|
|
|
|
while(waserror())
|
|
;
|
|
|
|
/* wait for reader to start */
|
|
while(er->readp == (void*)-1)
|
|
tsleep(&up->sleep, return0, 0, 300);
|
|
|
|
if(er->readp != nil)
|
|
postnote(er->readp, 1, "unbind", 0);
|
|
|
|
poperror();
|
|
|
|
wunlock(ifc);
|
|
while(waserror())
|
|
;
|
|
|
|
/* wait for reader to die */
|
|
while(er->readp != nil)
|
|
tsleep(&up->sleep, return0, 0, 300);
|
|
|
|
poperror();
|
|
wlock(ifc);
|
|
|
|
if(er->mchan != nil)
|
|
cclose(er->mchan);
|
|
|
|
free(er);
|
|
}
|
|
|
|
/*
|
|
* called by ipoput with a single block to write
|
|
*/
|
|
static void
|
|
netdevbwrite(Ipifc *ifc, Block *bp, int, uchar*)
|
|
{
|
|
Netdevrock *er = ifc->arg;
|
|
|
|
if(BLEN(bp) < ifc->mintu)
|
|
bp = adjustblock(bp, ifc->mintu);
|
|
|
|
devtab[er->mchan->type]->bwrite(er->mchan, bp, 0);
|
|
ifc->out++;
|
|
}
|
|
|
|
/*
|
|
* process to read from the device
|
|
*/
|
|
static void
|
|
netdevread(void *a)
|
|
{
|
|
Ipifc *ifc;
|
|
Block *bp;
|
|
Netdevrock *er;
|
|
|
|
ifc = a;
|
|
er = ifc->arg;
|
|
er->readp = up; /* hide identity under a rock for unbind */
|
|
if(!waserror())
|
|
for(;;){
|
|
bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxtu, 0);
|
|
if(bp == nil){
|
|
poperror();
|
|
if(!waserror()){
|
|
static char *argv[] = { "unbind" };
|
|
ifc->conv->p->ctl(ifc->conv, argv, 1);
|
|
}
|
|
break;
|
|
}
|
|
rlock(ifc);
|
|
if(waserror()){
|
|
runlock(ifc);
|
|
nexterror();
|
|
}
|
|
ifc->in++;
|
|
if(ifc->lifc == nil)
|
|
freeb(bp);
|
|
else
|
|
ipiput4(er->f, ifc, bp);
|
|
runlock(ifc);
|
|
poperror();
|
|
}
|
|
er->readp = nil;
|
|
pexit("hangup", 1);
|
|
}
|
|
|
|
void
|
|
netdevmediumlink(void)
|
|
{
|
|
addipmedium(&netdevmedium);
|
|
}
|