| #include "stdinc.h" | 
 |  | 
 | #include "9.h" | 
 |  | 
 | static struct { | 
 | 	QLock	lock; | 
 |  | 
 | 	Fid*	free; | 
 | 	int	nfree; | 
 | 	int	inuse; | 
 | } fbox; | 
 |  | 
 | static void | 
 | fidLock(Fid* fid, int flags) | 
 | { | 
 | 	if(flags & FidFWlock){ | 
 | 		wlock(&fid->lock); | 
 | 		fid->flags = flags; | 
 | 	} | 
 | 	else | 
 | 		rlock(&fid->lock); | 
 |  | 
 | 	/* | 
 | 	 * Callers of file* routines are expected to lock fsys->fs->elk | 
 | 	 * before making any calls in order to make sure the epoch doesn't | 
 | 	 * change underfoot. With the exception of Tversion and Tattach, | 
 | 	 * that implies all 9P functions need to lock on entry and unlock | 
 | 	 * on exit. Fortunately, the general case is the 9P functions do | 
 | 	 * fidGet on entry and fidPut on exit, so this is a convenient place | 
 | 	 * to do the locking. | 
 | 	 * No fsys->fs->elk lock is required if the fid is being created | 
 | 	 * (Tauth, Tattach and Twalk). FidFCreate is always accompanied by | 
 | 	 * FidFWlock so the setting and testing of FidFCreate here and in | 
 | 	 * fidUnlock below is always done under fid->lock. | 
 | 	 * A side effect is that fidFree is called with the fid locked, and | 
 | 	 * must call fidUnlock only after it has disposed of any File | 
 | 	 * resources still held. | 
 | 	 */ | 
 | 	if(!(flags & FidFCreate)) | 
 | 		fsysFsRlock(fid->fsys); | 
 | } | 
 |  | 
 | static void | 
 | fidUnlock(Fid* fid) | 
 | { | 
 | 	if(!(fid->flags & FidFCreate)) | 
 | 		fsysFsRUnlock(fid->fsys); | 
 | 	if(fid->flags & FidFWlock){ | 
 | 		fid->flags = 0; | 
 | 		wunlock(&fid->lock); | 
 | 		return; | 
 | 	} | 
 | 	runlock(&fid->lock); | 
 | } | 
 |  | 
 | static Fid* | 
 | fidAlloc(void) | 
 | { | 
 | 	Fid *fid; | 
 |  | 
 | 	qlock(&fbox.lock); | 
 | 	if(fbox.nfree > 0){ | 
 | 		fid = fbox.free; | 
 | 		fbox.free = fid->hash; | 
 | 		fbox.nfree--; | 
 | 	} | 
 | 	else{ | 
 | 		fid = vtmallocz(sizeof(Fid)); | 
 | 	} | 
 | 	fbox.inuse++; | 
 | 	qunlock(&fbox.lock); | 
 |  | 
 | 	fid->con = nil; | 
 | 	fid->fidno = NOFID; | 
 | 	fid->ref = 0; | 
 | 	fid->flags = 0; | 
 | 	fid->open = FidOCreate; | 
 | 	assert(fid->fsys == nil); | 
 | 	assert(fid->file == nil); | 
 | 	fid->qid = (Qid){0, 0, 0}; | 
 | 	assert(fid->uid == nil); | 
 | 	assert(fid->uname == nil); | 
 | 	assert(fid->db == nil); | 
 | 	assert(fid->excl == nil); | 
 | 	assert(fid->rpc == nil); | 
 | 	assert(fid->cuname == nil); | 
 | 	fid->hash = fid->next = fid->prev = nil; | 
 |  | 
 | 	return fid; | 
 | } | 
 |  | 
 | static void | 
 | fidFree(Fid* fid) | 
 | { | 
 | 	if(fid->file != nil){ | 
 | 		fileDecRef(fid->file); | 
 | 		fid->file = nil; | 
 | 	} | 
 | 	if(fid->db != nil){ | 
 | 		dirBufFree(fid->db); | 
 | 		fid->db = nil; | 
 | 	} | 
 | 	fidUnlock(fid); | 
 |  | 
 | 	if(fid->uid != nil){ | 
 | 		vtfree(fid->uid); | 
 | 		fid->uid = nil; | 
 | 	} | 
 | 	if(fid->uname != nil){ | 
 | 		vtfree(fid->uname); | 
 | 		fid->uname = nil; | 
 | 	} | 
 | 	if(fid->excl != nil) | 
 | 		exclFree(fid); | 
 | 	if(fid->rpc != nil){ | 
 | 		close(fid->rpc->afd); | 
 | 		auth_freerpc(fid->rpc); | 
 | 		fid->rpc = nil; | 
 | 	} | 
 | 	if(fid->fsys != nil){ | 
 | 		fsysPut(fid->fsys); | 
 | 		fid->fsys = nil; | 
 | 	} | 
 | 	if(fid->cuname != nil){ | 
 | 		vtfree(fid->cuname); | 
 | 		fid->cuname = nil; | 
 | 	} | 
 |  | 
 | 	qlock(&fbox.lock); | 
 | 	fbox.inuse--; | 
 | 	if(fbox.nfree < 10){ | 
 | 		fid->hash = fbox.free; | 
 | 		fbox.free = fid; | 
 | 		fbox.nfree++; | 
 | 	} | 
 | 	else{ | 
 | 		vtfree(fid); | 
 | 	} | 
 | 	qunlock(&fbox.lock); | 
 | } | 
 |  | 
 | static void | 
 | fidUnHash(Fid* fid) | 
 | { | 
 | 	Fid *fp, **hash; | 
 |  | 
 | 	assert(fid->ref == 0); | 
 |  | 
 | 	hash = &fid->con->fidhash[fid->fidno % NFidHash]; | 
 | 	for(fp = *hash; fp != nil; fp = fp->hash){ | 
 | 		if(fp == fid){ | 
 | 			*hash = fp->hash; | 
 | 			break; | 
 | 		} | 
 | 		hash = &fp->hash; | 
 | 	} | 
 | 	assert(fp == fid); | 
 |  | 
 | 	if(fid->prev != nil) | 
 | 		fid->prev->next = fid->next; | 
 | 	else | 
 | 		fid->con->fhead = fid->next; | 
 | 	if(fid->next != nil) | 
 | 		fid->next->prev = fid->prev; | 
 | 	else | 
 | 		fid->con->ftail = fid->prev; | 
 | 	fid->prev = fid->next = nil; | 
 |  | 
 | 	fid->con->nfid--; | 
 | } | 
 |  | 
 | Fid* | 
 | fidGet(Con* con, u32int fidno, int flags) | 
 | { | 
 | 	Fid *fid, **hash; | 
 |  | 
 | 	if(fidno == NOFID) | 
 | 		return nil; | 
 |  | 
 | 	hash = &con->fidhash[fidno % NFidHash]; | 
 | 	qlock(&con->fidlock); | 
 | 	for(fid = *hash; fid != nil; fid = fid->hash){ | 
 | 		if(fid->fidno != fidno) | 
 | 			continue; | 
 |  | 
 | 		/* | 
 | 		 * Already in use is an error | 
 | 		 * when called from attach, clone or walk. | 
 | 		 */ | 
 | 		if(flags & FidFCreate){ | 
 | 			qunlock(&con->fidlock); | 
 | 			werrstr("%s: fid 0x%ud in use", argv0, fidno); | 
 | 			return nil; | 
 | 		} | 
 | 		fid->ref++; | 
 | 		qunlock(&con->fidlock); | 
 |  | 
 | 		fidLock(fid, flags); | 
 | 		if((fid->open & FidOCreate) || fid->fidno == NOFID){ | 
 | 			fidPut(fid); | 
 | 			werrstr("%s: fid invalid", argv0); | 
 | 			return nil; | 
 | 		} | 
 | 		return fid; | 
 | 	} | 
 |  | 
 | 	if((flags & FidFCreate) && (fid = fidAlloc()) != nil){ | 
 | 		assert(flags & FidFWlock); | 
 | 		fid->con = con; | 
 | 		fid->fidno = fidno; | 
 | 		fid->ref = 1; | 
 |  | 
 | 		fid->hash = *hash; | 
 | 		*hash = fid; | 
 | 		if(con->ftail != nil){ | 
 | 			fid->prev = con->ftail; | 
 | 			con->ftail->next = fid; | 
 | 		} | 
 | 		else{ | 
 | 			con->fhead = fid; | 
 | 			fid->prev = nil; | 
 | 		} | 
 | 		con->ftail = fid; | 
 | 		fid->next = nil; | 
 |  | 
 | 		con->nfid++; | 
 | 		qunlock(&con->fidlock); | 
 |  | 
 | 		/* | 
 | 		 * The FidOCreate flag is used to prevent any | 
 | 		 * accidental access to the Fid between unlocking the | 
 | 		 * hash and acquiring the Fid lock for return. | 
 | 		 */ | 
 | 		fidLock(fid, flags); | 
 | 		fid->open &= ~FidOCreate; | 
 | 		return fid; | 
 | 	} | 
 | 	qunlock(&con->fidlock); | 
 |  | 
 | 	werrstr("%s: fid not found", argv0); | 
 | 	return nil; | 
 | } | 
 |  | 
 | void | 
 | fidPut(Fid* fid) | 
 | { | 
 | 	qlock(&fid->con->fidlock); | 
 | 	assert(fid->ref > 0); | 
 | 	fid->ref--; | 
 | 	qunlock(&fid->con->fidlock); | 
 |  | 
 | 	if(fid->ref == 0 && fid->fidno == NOFID){ | 
 | 		fidFree(fid); | 
 | 		return; | 
 | 	} | 
 | 	fidUnlock(fid); | 
 | } | 
 |  | 
 | void | 
 | fidClunk(Fid* fid) | 
 | { | 
 | 	assert(fid->flags & FidFWlock); | 
 |  | 
 | 	qlock(&fid->con->fidlock); | 
 | 	assert(fid->ref > 0); | 
 | 	fid->ref--; | 
 | 	fidUnHash(fid); | 
 | 	fid->fidno = NOFID; | 
 | 	qunlock(&fid->con->fidlock); | 
 |  | 
 | 	if(fid->ref > 0){ | 
 | 		/* not reached - fidUnHash requires ref == 0 */ | 
 | 		fidUnlock(fid); | 
 | 		return; | 
 | 	} | 
 | 	fidFree(fid); | 
 | } | 
 |  | 
 | void | 
 | fidClunkAll(Con* con) | 
 | { | 
 | 	Fid *fid; | 
 | 	u32int fidno; | 
 |  | 
 | 	qlock(&con->fidlock); | 
 | 	while(con->fhead != nil){ | 
 | 		fidno = con->fhead->fidno; | 
 | 		qunlock(&con->fidlock); | 
 | 		if((fid = fidGet(con, fidno, FidFWlock)) != nil) | 
 | 			fidClunk(fid); | 
 | 		qlock(&con->fidlock); | 
 | 	} | 
 | 	qunlock(&con->fidlock); | 
 | } | 
 |  | 
 | void | 
 | fidInit(void) | 
 | { | 
 | } |