xref: /plan9/sys/src/cmd/fossil/9fid.c (revision 36066be07288900642605e3e4f42ceece4e473a9)
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