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 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 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* 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 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 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* 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("fid 0x%ud in use", 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("fid invalid"); 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("fid not found"); 245 return nil; 246 } 247 248 void 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 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 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 301 fidInit(void) 302 { 303 fbox.lock = vtLockAlloc(); 304 } 305