/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include "stdinc.h" #include "9.h" static struct { VtLock* lock; Fid* free; int nfree; int inuse;} fbox; static voidfidLock(Fid* fid, int flags){ if(flags & FidFWlock){ vtLock(fid->lock); fid->flags = flags; } else vtRLock(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 voidfidUnlock(Fid* fid){ if(!(fid->flags & FidFCreate)) fsysFsRUnlock(fid->fsys); if(fid->flags & FidFWlock){ fid->flags = 0; vtUnlock(fid->lock); return; } vtRUnlock(fid->lock);} static Fid*fidAlloc(void){ Fid *fid; vtLock(fbox.lock); if(fbox.nfree > 0){ fid = fbox.free; fbox.free = fid->hash; fbox.nfree--; } else{ fid = vtMemAllocZ(sizeof(Fid)); fid->lock = vtLockAlloc(); fid->alock = vtLockAlloc(); } fbox.inuse++; vtUnlock(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 voidfidFree(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){ vtMemFree(fid->uid); fid->uid = nil; } if(fid->uname != nil){ vtMemFree(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){ vtMemFree(fid->cuname); fid->cuname = nil; } vtLock(fbox.lock); fbox.inuse--; if(fbox.nfree < 10){ fid->hash = fbox.free; fbox.free = fid; fbox.nfree++; } else{ vtLockFree(fid->alock); vtLockFree(fid->lock); vtMemFree(fid); } vtUnlock(fbox.lock);} static voidfidUnHash(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]; vtLock(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){ vtUnlock(con->fidlock); vtSetError("%s: fid 0x%ud in use", argv0, fidno); return nil; } fid->ref++; vtUnlock(con->fidlock); fidLock(fid, flags); if((fid->open & FidOCreate) || fid->fidno == NOFID){ fidPut(fid); vtSetError("%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++; vtUnlock(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; } vtUnlock(con->fidlock); vtSetError("%s: fid not found", argv0); return nil;} voidfidPut(Fid* fid){ vtLock(fid->con->fidlock); assert(fid->ref > 0); fid->ref--; vtUnlock(fid->con->fidlock); if(fid->ref == 0 && fid->fidno == NOFID){ fidFree(fid); return; } fidUnlock(fid);} voidfidClunk(Fid* fid){ assert(fid->flags & FidFWlock); vtLock(fid->con->fidlock); assert(fid->ref > 0); fid->ref--; fidUnHash(fid); fid->fidno = NOFID; vtUnlock(fid->con->fidlock); if(fid->ref > 0){ /* not reached - fidUnHash requires ref == 0 */ fidUnlock(fid); return; } fidFree(fid);} voidfidClunkAll(Con* con){ Fid *fid; u32int fidno; vtLock(con->fidlock); while(con->fhead != nil){ fidno = con->fhead->fidno; vtUnlock(con->fidlock); if((fid = fidGet(con, fidno, FidFWlock)) != nil) fidClunk(fid); vtLock(con->fidlock); } vtUnlock(con->fidlock);} voidfidInit(void){ fbox.lock = vtLockAlloc();}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include "stdinc.h"
#include "9.h"
static struct {
VtLock* lock;
Fid* free;
int nfree;
int inuse;
} fbox;
static void
fidLock(Fid* fid, int flags)
{
if(flags & FidFWlock){
vtLock(fid->lock);
fid->flags = flags;
}
else
vtRLock(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;
vtUnlock(fid->lock);
return;
}
vtRUnlock(fid->lock);
}
static Fid*
fidAlloc(void)
{
Fid *fid;
vtLock(fbox.lock);
if(fbox.nfree > 0){
fid = fbox.free;
fbox.free = fid->hash;
fbox.nfree--;
}
else{
fid = vtMemAllocZ(sizeof(Fid));
fid->lock = vtLockAlloc();
fid->alock = vtLockAlloc();
}
fbox.inuse++;
vtUnlock(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){
vtMemFree(fid->uid);
fid->uid = nil;
}
if(fid->uname != nil){
vtMemFree(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){
vtMemFree(fid->cuname);
fid->cuname = nil;
}
vtLock(fbox.lock);
fbox.inuse--;
if(fbox.nfree < 10){
fid->hash = fbox.free;
fbox.free = fid;
fbox.nfree++;
}
else{
vtLockFree(fid->alock);
vtLockFree(fid->lock);
vtMemFree(fid);
}
vtUnlock(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];
vtLock(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){
vtUnlock(con->fidlock);
vtSetError("%s: fid 0x%ud in use", argv0, fidno);
return nil;
}
fid->ref++;
vtUnlock(con->fidlock);
fidLock(fid, flags);
if((fid->open & FidOCreate) || fid->fidno == NOFID){
fidPut(fid);
vtSetError("%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++;
vtUnlock(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;
}
vtUnlock(con->fidlock);
vtSetError("%s: fid not found", argv0);
return nil;
}
void
fidPut(Fid* fid)
{
vtLock(fid->con->fidlock);
assert(fid->ref > 0);
fid->ref--;
vtUnlock(fid->con->fidlock);
if(fid->ref == 0 && fid->fidno == NOFID){
fidFree(fid);
return;
}
fidUnlock(fid);
}
void
fidClunk(Fid* fid)
{
assert(fid->flags & FidFWlock);
vtLock(fid->con->fidlock);
assert(fid->ref > 0);
fid->ref--;
fidUnHash(fid);
fid->fidno = NOFID;
vtUnlock(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;
vtLock(con->fidlock);
while(con->fhead != nil){
fidno = con->fhead->fidno;
vtUnlock(con->fidlock);
if((fid = fidGet(con, fidno, FidFWlock)) != nil)
fidClunk(fid);
vtLock(con->fidlock);
}
vtUnlock(con->fidlock);
}
void
fidInit(void)
{
fbox.lock = vtLockAlloc();
}