blob: 49fab0b9bc2d61f58cae8a452be838c5dd9f9415 [file] [log] [blame]
#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)
{
}