1 #include "stdinc.h"
2
3 #include "9.h"
4
5 static struct {
6 VtLock* lock;
7
8 Fid* free;
9 int nfree;
10 int inuse;
11 } fbox;
12
13 static void
fidLock(Fid * fid,int flags)14 fidLock(Fid* fid, int flags)
15 {
16 if(flags & FidFWlock){
17 vtLock(fid->lock);
18 fid->flags = flags;
19 }
20 else
21 vtRLock(fid->lock);
22
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 }
42
43 static void
fidUnlock(Fid * fid)44 fidUnlock(Fid* fid)
45 {
46 if(!(fid->flags & FidFCreate))
47 fsysFsRUnlock(fid->fsys);
48 if(fid->flags & FidFWlock){
49 fid->flags = 0;
50 vtUnlock(fid->lock);
51 return;
52 }
53 vtRUnlock(fid->lock);
54 }
55
56 static Fid*
fidAlloc(void)57 fidAlloc(void)
58 {
59 Fid *fid;
60
61 vtLock(fbox.lock);
62 if(fbox.nfree > 0){
63 fid = fbox.free;
64 fbox.free = fid->hash;
65 fbox.nfree--;
66 }
67 else{
68 fid = vtMemAllocZ(sizeof(Fid));
69 fid->lock = vtLockAlloc();
70 fid->alock = vtLockAlloc();
71 }
72 fbox.inuse++;
73 vtUnlock(fbox.lock);
74
75 fid->con = nil;
76 fid->fidno = NOFID;
77 fid->ref = 0;
78 fid->flags = 0;
79 fid->open = FidOCreate;
80 assert(fid->fsys == nil);
81 assert(fid->file == nil);
82 fid->qid = (Qid){0, 0, 0};
83 assert(fid->uid == nil);
84 assert(fid->uname == nil);
85 assert(fid->db == nil);
86 assert(fid->excl == nil);
87 assert(fid->rpc == nil);
88 assert(fid->cuname == nil);
89 fid->hash = fid->next = fid->prev = nil;
90
91 return fid;
92 }
93
94 static void
fidFree(Fid * fid)95 fidFree(Fid* fid)
96 {
97 if(fid->file != nil){
98 fileDecRef(fid->file);
99 fid->file = nil;
100 }
101 if(fid->db != nil){
102 dirBufFree(fid->db);
103 fid->db = nil;
104 }
105 fidUnlock(fid);
106
107 if(fid->uid != nil){
108 vtMemFree(fid->uid);
109 fid->uid = nil;
110 }
111 if(fid->uname != nil){
112 vtMemFree(fid->uname);
113 fid->uname = nil;
114 }
115 if(fid->excl != nil)
116 exclFree(fid);
117 if(fid->rpc != nil){
118 close(fid->rpc->afd);
119 auth_freerpc(fid->rpc);
120 fid->rpc = nil;
121 }
122 if(fid->fsys != nil){
123 fsysPut(fid->fsys);
124 fid->fsys = nil;
125 }
126 if(fid->cuname != nil){
127 vtMemFree(fid->cuname);
128 fid->cuname = nil;
129 }
130
131 vtLock(fbox.lock);
132 fbox.inuse--;
133 if(fbox.nfree < 10){
134 fid->hash = fbox.free;
135 fbox.free = fid;
136 fbox.nfree++;
137 }
138 else{
139 vtLockFree(fid->alock);
140 vtLockFree(fid->lock);
141 vtMemFree(fid);
142 }
143 vtUnlock(fbox.lock);
144 }
145
146 static void
fidUnHash(Fid * fid)147 fidUnHash(Fid* fid)
148 {
149 Fid *fp, **hash;
150
151 assert(fid->ref == 0);
152
153 hash = &fid->con->fidhash[fid->fidno % NFidHash];
154 for(fp = *hash; fp != nil; fp = fp->hash){
155 if(fp == fid){
156 *hash = fp->hash;
157 break;
158 }
159 hash = &fp->hash;
160 }
161 assert(fp == fid);
162
163 if(fid->prev != nil)
164 fid->prev->next = fid->next;
165 else
166 fid->con->fhead = fid->next;
167 if(fid->next != nil)
168 fid->next->prev = fid->prev;
169 else
170 fid->con->ftail = fid->prev;
171 fid->prev = fid->next = nil;
172
173 fid->con->nfid--;
174 }
175
176 Fid*
fidGet(Con * con,u32int fidno,int flags)177 fidGet(Con* con, u32int fidno, int flags)
178 {
179 Fid *fid, **hash;
180
181 if(fidno == NOFID)
182 return nil;
183
184 hash = &con->fidhash[fidno % NFidHash];
185 vtLock(con->fidlock);
186 for(fid = *hash; fid != nil; fid = fid->hash){
187 if(fid->fidno != fidno)
188 continue;
189
190 /*
191 * Already in use is an error
192 * when called from attach, clone or walk.
193 */
194 if(flags & FidFCreate){
195 vtUnlock(con->fidlock);
196 vtSetError("%s: fid 0x%ud in use", argv0, fidno);
197 return nil;
198 }
199 fid->ref++;
200 vtUnlock(con->fidlock);
201
202 fidLock(fid, flags);
203 if((fid->open & FidOCreate) || fid->fidno == NOFID){
204 fidPut(fid);
205 vtSetError("%s: fid invalid", argv0);
206 return nil;
207 }
208 return fid;
209 }
210
211 if((flags & FidFCreate) && (fid = fidAlloc()) != nil){
212 assert(flags & FidFWlock);
213 fid->con = con;
214 fid->fidno = fidno;
215 fid->ref = 1;
216
217 fid->hash = *hash;
218 *hash = fid;
219 if(con->ftail != nil){
220 fid->prev = con->ftail;
221 con->ftail->next = fid;
222 }
223 else{
224 con->fhead = fid;
225 fid->prev = nil;
226 }
227 con->ftail = fid;
228 fid->next = nil;
229
230 con->nfid++;
231 vtUnlock(con->fidlock);
232
233 /*
234 * The FidOCreate flag is used to prevent any
235 * accidental access to the Fid between unlocking the
236 * hash and acquiring the Fid lock for return.
237 */
238 fidLock(fid, flags);
239 fid->open &= ~FidOCreate;
240 return fid;
241 }
242 vtUnlock(con->fidlock);
243
244 vtSetError("%s: fid not found", argv0);
245 return nil;
246 }
247
248 void
fidPut(Fid * fid)249 fidPut(Fid* fid)
250 {
251 vtLock(fid->con->fidlock);
252 assert(fid->ref > 0);
253 fid->ref--;
254 vtUnlock(fid->con->fidlock);
255
256 if(fid->ref == 0 && fid->fidno == NOFID){
257 fidFree(fid);
258 return;
259 }
260 fidUnlock(fid);
261 }
262
263 void
fidClunk(Fid * fid)264 fidClunk(Fid* fid)
265 {
266 assert(fid->flags & FidFWlock);
267
268 vtLock(fid->con->fidlock);
269 assert(fid->ref > 0);
270 fid->ref--;
271 fidUnHash(fid);
272 fid->fidno = NOFID;
273 vtUnlock(fid->con->fidlock);
274
275 if(fid->ref > 0){
276 /* not reached - fidUnHash requires ref == 0 */
277 fidUnlock(fid);
278 return;
279 }
280 fidFree(fid);
281 }
282
283 void
fidClunkAll(Con * con)284 fidClunkAll(Con* con)
285 {
286 Fid *fid;
287 u32int fidno;
288
289 vtLock(con->fidlock);
290 while(con->fhead != nil){
291 fidno = con->fhead->fidno;
292 vtUnlock(con->fidlock);
293 if((fid = fidGet(con, fidno, FidFWlock)) != nil)
294 fidClunk(fid);
295 vtLock(con->fidlock);
296 }
297 vtUnlock(con->fidlock);
298 }
299
300 void
fidInit(void)301 fidInit(void)
302 {
303 fbox.lock = vtLockAlloc();
304 }
305