Blob


1 #include "stdinc.h"
3 #include "9.h"
5 static struct {
6 QLock lock;
8 Fid* free;
9 int nfree;
10 int inuse;
11 } fbox;
13 static void
14 fidLock(Fid* fid, int flags)
15 {
16 if(flags & FidFWlock){
17 wlock(&fid->lock);
18 fid->flags = flags;
19 }
20 else
21 rlock(&fid->lock);
23 /*
24 * Callers of file* routines are expected to lock fsys->fs->elk
25 * before making any calls in order to make sure the epoch doesn't
26 * change underfoot. With the exception of Tversion and Tattach,
27 * that implies all 9P functions need to lock on entry and unlock
28 * on exit. Fortunately, the general case is the 9P functions do
29 * fidGet on entry and fidPut on exit, so this is a convenient place
30 * to do the locking.
31 * No fsys->fs->elk lock is required if the fid is being created
32 * (Tauth, Tattach and Twalk). FidFCreate is always accompanied by
33 * FidFWlock so the setting and testing of FidFCreate here and in
34 * fidUnlock below is always done under fid->lock.
35 * A side effect is that fidFree is called with the fid locked, and
36 * must call fidUnlock only after it has disposed of any File
37 * resources still held.
38 */
39 if(!(flags & FidFCreate))
40 fsysFsRlock(fid->fsys);
41 }
43 static void
44 fidUnlock(Fid* fid)
45 {
46 if(!(fid->flags & FidFCreate))
47 fsysFsRUnlock(fid->fsys);
48 if(fid->flags & FidFWlock){
49 fid->flags = 0;
50 wunlock(&fid->lock);
51 return;
52 }
53 runlock(&fid->lock);
54 }
56 static Fid*
57 fidAlloc(void)
58 {
59 Fid *fid;
61 qlock(&fbox.lock);
62 if(fbox.nfree > 0){
63 fid = fbox.free;
64 fbox.free = fid->hash;
65 fbox.nfree--;
66 }
67 else{
68 fid = vtmallocz(sizeof(Fid));
69 }
70 fbox.inuse++;
71 qunlock(&fbox.lock);
73 fid->con = nil;
74 fid->fidno = NOFID;
75 fid->ref = 0;
76 fid->flags = 0;
77 fid->open = FidOCreate;
78 assert(fid->fsys == nil);
79 assert(fid->file == nil);
80 fid->qid = (Qid){0, 0, 0};
81 assert(fid->uid == nil);
82 assert(fid->uname == nil);
83 assert(fid->db == nil);
84 assert(fid->excl == nil);
85 assert(fid->rpc == nil);
86 assert(fid->cuname == nil);
87 fid->hash = fid->next = fid->prev = nil;
89 return fid;
90 }
92 static void
93 fidFree(Fid* fid)
94 {
95 if(fid->file != nil){
96 fileDecRef(fid->file);
97 fid->file = nil;
98 }
99 if(fid->db != nil){
100 dirBufFree(fid->db);
101 fid->db = nil;
103 fidUnlock(fid);
105 if(fid->uid != nil){
106 vtfree(fid->uid);
107 fid->uid = nil;
109 if(fid->uname != nil){
110 vtfree(fid->uname);
111 fid->uname = nil;
113 if(fid->excl != nil)
114 exclFree(fid);
115 if(fid->rpc != nil){
116 close(fid->rpc->afd);
117 auth_freerpc(fid->rpc);
118 fid->rpc = nil;
120 if(fid->fsys != nil){
121 fsysPut(fid->fsys);
122 fid->fsys = nil;
124 if(fid->cuname != nil){
125 vtfree(fid->cuname);
126 fid->cuname = nil;
129 qlock(&fbox.lock);
130 fbox.inuse--;
131 if(fbox.nfree < 10){
132 fid->hash = fbox.free;
133 fbox.free = fid;
134 fbox.nfree++;
136 else{
137 vtfree(fid);
139 qunlock(&fbox.lock);
142 static void
143 fidUnHash(Fid* fid)
145 Fid *fp, **hash;
147 assert(fid->ref == 0);
149 hash = &fid->con->fidhash[fid->fidno % NFidHash];
150 for(fp = *hash; fp != nil; fp = fp->hash){
151 if(fp == fid){
152 *hash = fp->hash;
153 break;
155 hash = &fp->hash;
157 assert(fp == fid);
159 if(fid->prev != nil)
160 fid->prev->next = fid->next;
161 else
162 fid->con->fhead = fid->next;
163 if(fid->next != nil)
164 fid->next->prev = fid->prev;
165 else
166 fid->con->ftail = fid->prev;
167 fid->prev = fid->next = nil;
169 fid->con->nfid--;
172 Fid*
173 fidGet(Con* con, u32int fidno, int flags)
175 Fid *fid, **hash;
177 if(fidno == NOFID)
178 return nil;
180 hash = &con->fidhash[fidno % NFidHash];
181 qlock(&con->fidlock);
182 for(fid = *hash; fid != nil; fid = fid->hash){
183 if(fid->fidno != fidno)
184 continue;
186 /*
187 * Already in use is an error
188 * when called from attach, clone or walk.
189 */
190 if(flags & FidFCreate){
191 qunlock(&con->fidlock);
192 werrstr("%s: fid 0x%ud in use", argv0, fidno);
193 return nil;
195 fid->ref++;
196 qunlock(&con->fidlock);
198 fidLock(fid, flags);
199 if((fid->open & FidOCreate) || fid->fidno == NOFID){
200 fidPut(fid);
201 werrstr("%s: fid invalid", argv0);
202 return nil;
204 return fid;
207 if((flags & FidFCreate) && (fid = fidAlloc()) != nil){
208 assert(flags & FidFWlock);
209 fid->con = con;
210 fid->fidno = fidno;
211 fid->ref = 1;
213 fid->hash = *hash;
214 *hash = fid;
215 if(con->ftail != nil){
216 fid->prev = con->ftail;
217 con->ftail->next = fid;
219 else{
220 con->fhead = fid;
221 fid->prev = nil;
223 con->ftail = fid;
224 fid->next = nil;
226 con->nfid++;
227 qunlock(&con->fidlock);
229 /*
230 * The FidOCreate flag is used to prevent any
231 * accidental access to the Fid between unlocking the
232 * hash and acquiring the Fid lock for return.
233 */
234 fidLock(fid, flags);
235 fid->open &= ~FidOCreate;
236 return fid;
238 qunlock(&con->fidlock);
240 werrstr("%s: fid not found", argv0);
241 return nil;
244 void
245 fidPut(Fid* fid)
247 qlock(&fid->con->fidlock);
248 assert(fid->ref > 0);
249 fid->ref--;
250 qunlock(&fid->con->fidlock);
252 if(fid->ref == 0 && fid->fidno == NOFID){
253 fidFree(fid);
254 return;
256 fidUnlock(fid);
259 void
260 fidClunk(Fid* fid)
262 assert(fid->flags & FidFWlock);
264 qlock(&fid->con->fidlock);
265 assert(fid->ref > 0);
266 fid->ref--;
267 fidUnHash(fid);
268 fid->fidno = NOFID;
269 qunlock(&fid->con->fidlock);
271 if(fid->ref > 0){
272 /* not reached - fidUnHash requires ref == 0 */
273 fidUnlock(fid);
274 return;
276 fidFree(fid);
279 void
280 fidClunkAll(Con* con)
282 Fid *fid;
283 u32int fidno;
285 qlock(&con->fidlock);
286 while(con->fhead != nil){
287 fidno = con->fhead->fidno;
288 qunlock(&con->fidlock);
289 if((fid = fidGet(con, fidno, FidFWlock)) != nil)
290 fidClunk(fid);
291 qlock(&con->fidlock);
293 qunlock(&con->fidlock);
296 void
297 fidInit(void)