Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | * | |
9 | * Portions Copyright (c) 2005-2008 Sine Nomine Associates | |
10 | */ | |
11 | ||
12 | /* | |
13 | System: VICE-TWO | |
14 | Module: vnode.c | |
15 | Institution: The Information Technology Center, Carnegie-Mellon University | |
16 | ||
17 | */ | |
18 | #include <afsconfig.h> | |
19 | #include <afs/param.h> | |
20 | ||
21 | #include <roken.h> | |
22 | ||
23 | #include <limits.h> | |
24 | ||
25 | #ifdef HAVE_SYS_FILE_H | |
26 | #include <sys/file.h> | |
27 | #endif | |
28 | ||
29 | #include <afs/opr.h> | |
30 | #ifdef AFS_PTHREAD_ENV | |
31 | #include <opr/lock.h> | |
32 | #endif | |
33 | #include <opr/jhash.h> | |
34 | #include "rx/rx_queue.h" | |
35 | #include <afs/afsint.h> | |
36 | #include "nfs.h" | |
37 | #include <afs/errors.h> | |
38 | #include "lock.h" | |
39 | #include "lwp.h" | |
40 | #include <afs/afssyscalls.h> | |
41 | #include "ihandle.h" | |
42 | #include "vnode.h" | |
43 | #include "volume.h" | |
44 | #include "volume_inline.h" | |
45 | #include "vnode_inline.h" | |
46 | #include "partition.h" | |
47 | #include "salvsync.h" | |
48 | #include "common.h" | |
49 | #ifdef AFS_NT40_ENV | |
50 | #include "ntops.h" | |
51 | #endif | |
52 | ||
53 | struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES]; | |
54 | ||
55 | void VNLog(afs_int32 aop, afs_int32 anparms, ... ); | |
56 | ||
57 | /* logging stuff for finding bugs */ | |
58 | #define THELOGSIZE 5120 | |
59 | static afs_int32 theLog[THELOGSIZE]; | |
60 | static afs_int32 vnLogPtr = 0; | |
61 | void | |
62 | VNLog(afs_int32 aop, afs_int32 anparms, ... ) | |
63 | { | |
64 | afs_int32 temp; | |
65 | va_list ap; | |
66 | ||
67 | va_start(ap, anparms); | |
68 | ||
69 | if (anparms > 4) | |
70 | anparms = 4; /* do bounds checking */ | |
71 | ||
72 | temp = (aop << 16) | anparms; | |
73 | theLog[vnLogPtr++] = temp; | |
74 | if (vnLogPtr >= THELOGSIZE) | |
75 | vnLogPtr = 0; | |
76 | for (temp = 0; temp < anparms; temp++) { | |
77 | theLog[vnLogPtr++] = va_arg(ap, afs_int32); | |
78 | if (vnLogPtr >= THELOGSIZE) | |
79 | vnLogPtr = 0; | |
80 | } | |
81 | va_end(ap); | |
82 | } | |
83 | ||
84 | ||
85 | /* Vnode hash table. Just use the Jenkins hash of the vnode number, | |
86 | * with the volume ID as an initval because it's there. (That will | |
87 | * make the same vnode number in different volumes hash to a different | |
88 | * value, which would probably not even be a big deal anyway.) | |
89 | */ | |
90 | ||
91 | #define VNODE_HASH_TABLE_BITS 11 | |
92 | #define VNODE_HASH_TABLE_SIZE opr_jhash_size(VNODE_HASH_TABLE_BITS) | |
93 | #define VNODE_HASH_TABLE_MASK opr_jhash_mask(VNODE_HASH_TABLE_BITS) | |
94 | private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE]; | |
95 | #define VNODE_HASH(volumeptr,vnodenumber)\ | |
96 | (opr_jhash_int((vnodenumber), V_id((volumeptr))) & VNODE_HASH_TABLE_MASK) | |
97 | ||
98 | ||
99 | ||
100 | #define BAD_IGET -1000 | |
101 | ||
102 | /* There are two separate vnode queue types defined here: | |
103 | * Each hash conflict chain -- is singly linked, with a single head | |
104 | * pointer. New entries are added at the beginning. Old | |
105 | * entries are removed by linear search, which generally | |
106 | * only occurs after a disk read). | |
107 | * LRU chain -- is doubly linked, single head pointer. | |
108 | * Entries are added at the head, reclaimed from the tail, | |
109 | * or removed from anywhere in the queue. | |
110 | */ | |
111 | ||
112 | /** | |
113 | * add a vnode to the volume's vnode list. | |
114 | * | |
115 | * @param[in] vp volume object pointer | |
116 | * @param[in] vnp vnode object pointer | |
117 | * | |
118 | * @note for DAFS, it may seem like we should be acquiring a lightweight ref | |
119 | * on vp, but this would actually break things. Right now, this is ok | |
120 | * because we destroy all vnode cache contents during during volume | |
121 | * detach. | |
122 | * | |
123 | * @pre VOL_LOCK held | |
124 | * | |
125 | * @internal volume package internal use only | |
126 | */ | |
127 | void | |
128 | AddToVVnList(Volume * vp, Vnode * vnp) | |
129 | { | |
130 | if (queue_IsOnQueue(vnp)) | |
131 | return; | |
132 | ||
133 | Vn_volume(vnp) = vp; | |
134 | Vn_cacheCheck(vnp) = vp->cacheCheck; | |
135 | queue_Append(&vp->vnode_list, vnp); | |
136 | Vn_stateFlags(vnp) |= VN_ON_VVN; | |
137 | } | |
138 | ||
139 | /** | |
140 | * delete a vnode from the volume's vnode list. | |
141 | * | |
142 | * @pre VOL_LOCK held | |
143 | * | |
144 | * @internal volume package internal use only | |
145 | */ | |
146 | void | |
147 | DeleteFromVVnList(Vnode * vnp) | |
148 | { | |
149 | Vn_volume(vnp) = NULL; | |
150 | ||
151 | if (!queue_IsOnQueue(vnp)) | |
152 | return; | |
153 | ||
154 | queue_Remove(vnp); | |
155 | Vn_stateFlags(vnp) &= ~(VN_ON_VVN); | |
156 | } | |
157 | ||
158 | /** | |
159 | * add a vnode to the end of the lru. | |
160 | * | |
161 | * @param[in] vcp vnode class info object pointer | |
162 | * @param[in] vnp vnode object pointer | |
163 | * | |
164 | * @internal vnode package internal use only | |
165 | */ | |
166 | void | |
167 | AddToVnLRU(struct VnodeClassInfo * vcp, Vnode * vnp) | |
168 | { | |
169 | if (Vn_stateFlags(vnp) & VN_ON_LRU) { | |
170 | return; | |
171 | } | |
172 | ||
173 | /* Add it to the circular LRU list */ | |
174 | if (vcp->lruHead == NULL) | |
175 | Abort("VPutVnode: vcp->lruHead==NULL"); | |
176 | else { | |
177 | vnp->lruNext = vcp->lruHead; | |
178 | vnp->lruPrev = vcp->lruHead->lruPrev; | |
179 | vcp->lruHead->lruPrev = vnp; | |
180 | vnp->lruPrev->lruNext = vnp; | |
181 | vcp->lruHead = vnp; | |
182 | } | |
183 | ||
184 | /* If the vnode was just deleted, put it at the end of the chain so it | |
185 | * will be reused immediately */ | |
186 | if (vnp->delete) | |
187 | vcp->lruHead = vnp->lruNext; | |
188 | ||
189 | Vn_stateFlags(vnp) |= VN_ON_LRU; | |
190 | } | |
191 | ||
192 | /** | |
193 | * delete a vnode from the lru. | |
194 | * | |
195 | * @param[in] vcp vnode class info object pointer | |
196 | * @param[in] vnp vnode object pointer | |
197 | * | |
198 | * @internal vnode package internal use only | |
199 | */ | |
200 | void | |
201 | DeleteFromVnLRU(struct VnodeClassInfo * vcp, Vnode * vnp) | |
202 | { | |
203 | if (!(Vn_stateFlags(vnp) & VN_ON_LRU)) { | |
204 | return; | |
205 | } | |
206 | ||
207 | if (vnp == vcp->lruHead) | |
208 | vcp->lruHead = vcp->lruHead->lruNext; | |
209 | ||
210 | if ((vnp == vcp->lruHead) || | |
211 | (vcp->lruHead == NULL)) | |
212 | Abort("DeleteFromVnLRU: lru chain addled!\n"); | |
213 | ||
214 | vnp->lruPrev->lruNext = vnp->lruNext; | |
215 | vnp->lruNext->lruPrev = vnp->lruPrev; | |
216 | ||
217 | Vn_stateFlags(vnp) &= ~(VN_ON_LRU); | |
218 | } | |
219 | ||
220 | /** | |
221 | * add a vnode to the vnode hash table. | |
222 | * | |
223 | * @param[in] vnp vnode object pointer | |
224 | * | |
225 | * @pre VOL_LOCK held | |
226 | * | |
227 | * @post vnode on hash | |
228 | * | |
229 | * @internal vnode package internal use only | |
230 | */ | |
231 | void | |
232 | AddToVnHash(Vnode * vnp) | |
233 | { | |
234 | unsigned int newHash; | |
235 | ||
236 | if (!(Vn_stateFlags(vnp) & VN_ON_HASH)) { | |
237 | newHash = VNODE_HASH(Vn_volume(vnp), Vn_id(vnp)); | |
238 | vnp->hashNext = VnodeHashTable[newHash]; | |
239 | VnodeHashTable[newHash] = vnp; | |
240 | vnp->hashIndex = newHash; | |
241 | ||
242 | Vn_stateFlags(vnp) |= VN_ON_HASH; | |
243 | } | |
244 | } | |
245 | ||
246 | /** | |
247 | * delete a vnode from the vnode hash table. | |
248 | * | |
249 | * @param[in] vnp | |
250 | * @param[in] hash | |
251 | * | |
252 | * @pre VOL_LOCK held | |
253 | * | |
254 | * @post vnode removed from hash | |
255 | * | |
256 | * @internal vnode package internal use only | |
257 | */ | |
258 | void | |
259 | DeleteFromVnHash(Vnode * vnp) | |
260 | { | |
261 | Vnode * tvnp; | |
262 | ||
263 | if (Vn_stateFlags(vnp) & VN_ON_HASH) { | |
264 | tvnp = VnodeHashTable[vnp->hashIndex]; | |
265 | if (tvnp == vnp) | |
266 | VnodeHashTable[vnp->hashIndex] = vnp->hashNext; | |
267 | else { | |
268 | while (tvnp && tvnp->hashNext != vnp) | |
269 | tvnp = tvnp->hashNext; | |
270 | if (tvnp) | |
271 | tvnp->hashNext = vnp->hashNext; | |
272 | } | |
273 | ||
274 | vnp->hashNext = NULL; | |
275 | vnp->hashIndex = 0; | |
276 | Vn_stateFlags(vnp) &= ~(VN_ON_HASH); | |
277 | } | |
278 | } | |
279 | ||
280 | ||
281 | /** | |
282 | * invalidate a vnode cache entry. | |
283 | * | |
284 | * @param[in] avnode vnode object pointer | |
285 | * | |
286 | * @pre VOL_LOCK held | |
287 | * | |
288 | * @post vnode metadata invalidated. | |
289 | * vnode removed from hash table. | |
290 | * DAFS: vnode state set to VN_STATE_INVALID. | |
291 | * | |
292 | * @internal vnode package internal use only | |
293 | */ | |
294 | void | |
295 | VInvalidateVnode_r(struct Vnode *avnode) | |
296 | { | |
297 | avnode->changed_newTime = 0; /* don't let it get flushed out again */ | |
298 | avnode->changed_oldTime = 0; | |
299 | avnode->delete = 0; /* it isn't deleted, really */ | |
300 | avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */ | |
301 | DeleteFromVnHash(avnode); | |
302 | #ifdef AFS_DEMAND_ATTACH_FS | |
303 | VnChangeState_r(avnode, VN_STATE_INVALID); | |
304 | #endif | |
305 | } | |
306 | ||
307 | ||
308 | /** | |
309 | * initialize vnode cache for a given vnode class. | |
310 | * | |
311 | * @param[in] class vnode class | |
312 | * @param[in] nVnodes size of cache | |
313 | * | |
314 | * @post vnode cache allocated and initialized | |
315 | * | |
316 | * @internal volume package internal use only | |
317 | * | |
318 | * @note generally called by VInitVolumePackage_r | |
319 | * | |
320 | * @see VInitVolumePackage_r | |
321 | */ | |
322 | int | |
323 | VInitVnodes(VnodeClass class, int nVnodes) | |
324 | { | |
325 | byte *va; | |
326 | struct VnodeClassInfo *vcp = &VnodeClassInfo[class]; | |
327 | ||
328 | vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0; | |
329 | vcp->cacheSize = nVnodes; | |
330 | switch (class) { | |
331 | case vSmall: | |
332 | opr_Assert(CHECKSIZE_SMALLVNODE); | |
333 | vcp->lruHead = NULL; | |
334 | vcp->residentSize = SIZEOF_SMALLVNODE; | |
335 | vcp->diskSize = SIZEOF_SMALLDISKVNODE; | |
336 | vcp->magic = SMALLVNODEMAGIC; | |
337 | break; | |
338 | case vLarge: | |
339 | vcp->lruHead = NULL; | |
340 | vcp->residentSize = SIZEOF_LARGEVNODE; | |
341 | vcp->diskSize = SIZEOF_LARGEDISKVNODE; | |
342 | vcp->magic = LARGEVNODEMAGIC; | |
343 | break; | |
344 | } | |
345 | { | |
346 | int s = vcp->diskSize - 1; | |
347 | int n = 0; | |
348 | while (s) | |
349 | s >>= 1, n++; | |
350 | vcp->logSize = n; | |
351 | } | |
352 | ||
353 | if (nVnodes == 0) | |
354 | return 0; | |
355 | ||
356 | va = (byte *) calloc(nVnodes, vcp->residentSize); | |
357 | opr_Assert(va != NULL); | |
358 | while (nVnodes--) { | |
359 | Vnode *vnp = (Vnode *) va; | |
360 | Vn_refcount(vnp) = 0; /* no context switches */ | |
361 | Vn_stateFlags(vnp) |= VN_ON_LRU; | |
362 | #ifdef AFS_DEMAND_ATTACH_FS | |
363 | CV_INIT(&Vn_stateCV(vnp), "vnode state", CV_DEFAULT, 0); | |
364 | Vn_state(vnp) = VN_STATE_INVALID; | |
365 | Vn_readers(vnp) = 0; | |
366 | #else /* !AFS_DEMAND_ATTACH_FS */ | |
367 | Lock_Init(&vnp->lock); | |
368 | #endif /* !AFS_DEMAND_ATTACH_FS */ | |
369 | vnp->changed_oldTime = 0; | |
370 | vnp->changed_newTime = 0; | |
371 | Vn_volume(vnp) = NULL; | |
372 | Vn_cacheCheck(vnp) = 0; | |
373 | vnp->delete = Vn_id(vnp) = 0; | |
374 | #ifdef AFS_PTHREAD_ENV | |
375 | vnp->writer = (pthread_t) 0; | |
376 | #else /* AFS_PTHREAD_ENV */ | |
377 | vnp->writer = (PROCESS) 0; | |
378 | #endif /* AFS_PTHREAD_ENV */ | |
379 | vnp->hashIndex = 0; | |
380 | vnp->handle = NULL; | |
381 | Vn_class(vnp) = vcp; | |
382 | if (vcp->lruHead == NULL) | |
383 | vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp; | |
384 | else { | |
385 | vnp->lruNext = vcp->lruHead; | |
386 | vnp->lruPrev = vcp->lruHead->lruPrev; | |
387 | vcp->lruHead->lruPrev = vnp; | |
388 | vnp->lruPrev->lruNext = vnp; | |
389 | vcp->lruHead = vnp; | |
390 | } | |
391 | va += vcp->residentSize; | |
392 | } | |
393 | return 0; | |
394 | } | |
395 | ||
396 | ||
397 | /** | |
398 | * allocate an unused vnode from the lru chain. | |
399 | * | |
400 | * @param[in] vcp vnode class info object pointer | |
401 | * @param[in] vp volume pointer | |
402 | * @param[in] vnodeNumber new vnode number that the vnode will be used for | |
403 | * | |
404 | * @pre VOL_LOCK is held | |
405 | * | |
406 | * @post vnode object is removed from lru | |
407 | * vnode is disassociated with its old volume, and associated with its | |
408 | * new volume | |
409 | * vnode is removed from its old vnode hash table, and for DAFS, it is | |
410 | * added to its new hash table | |
411 | * state is set to VN_STATE_INVALID. | |
412 | * inode handle is released. | |
413 | * a reservation is held on the vnode object | |
414 | * | |
415 | * @note we traverse backwards along the lru circlist. It shouldn't | |
416 | * be necessary to specify that nUsers == 0 since if it is in the list, | |
417 | * nUsers should be 0. Things shouldn't be in lruq unless no one is | |
418 | * using them. | |
419 | * | |
420 | * @warning DAFS: VOL_LOCK is dropped while doing inode handle release | |
421 | * | |
422 | * @warning for non-DAFS, the vnode is _not_ hashed on the vnode hash table; | |
423 | * non-DAFS must hash the vnode itself after loading data | |
424 | * | |
425 | * @return vnode object pointer | |
426 | */ | |
427 | Vnode * | |
428 | VGetFreeVnode_r(struct VnodeClassInfo * vcp, struct Volume *vp, | |
429 | VnodeId vnodeNumber) | |
430 | { | |
431 | Vnode *vnp; | |
432 | ||
433 | vnp = vcp->lruHead->lruPrev; | |
434 | #ifdef AFS_DEMAND_ATTACH_FS | |
435 | if (Vn_refcount(vnp) != 0 || VnIsExclusiveState(Vn_state(vnp)) || | |
436 | Vn_readers(vnp) != 0) | |
437 | Abort("VGetFreeVnode_r: in-use vnode in lruq"); | |
438 | #else | |
439 | if (Vn_refcount(vnp) != 0 || CheckLock(&vnp->lock)) | |
440 | Abort("VGetFreeVnode_r: locked vnode in lruq"); | |
441 | #endif | |
442 | VNLog(1, 2, Vn_id(vnp), (intptr_t)vnp, 0, 0); | |
443 | ||
444 | /* | |
445 | * it's going to be overwritten soon enough. | |
446 | * remove from LRU, delete hash entry, and | |
447 | * disassociate from old parent volume before | |
448 | * we have a chance to drop the vol glock | |
449 | */ | |
450 | DeleteFromVnLRU(vcp, vnp); | |
451 | DeleteFromVnHash(vnp); | |
452 | if (Vn_volume(vnp)) { | |
453 | DeleteFromVVnList(vnp); | |
454 | } | |
455 | ||
456 | /* we must re-hash the vnp _before_ we drop the glock again; otherwise, | |
457 | * someone else might try to grab the same vnode id, and we'll both alloc | |
458 | * a vnode object for the same vn id, bypassing vnode locking */ | |
459 | Vn_id(vnp) = vnodeNumber; | |
460 | VnCreateReservation_r(vnp); | |
461 | AddToVVnList(vp, vnp); | |
462 | #ifdef AFS_DEMAND_ATTACH_FS | |
463 | AddToVnHash(vnp); | |
464 | #endif | |
465 | ||
466 | /* drop the file descriptor */ | |
467 | if (vnp->handle) { | |
468 | #ifdef AFS_DEMAND_ATTACH_FS | |
469 | VnChangeState_r(vnp, VN_STATE_RELEASING); | |
470 | VOL_UNLOCK; | |
471 | #endif | |
472 | /* release is, potentially, a highly latent operation due to a couple | |
473 | * factors: | |
474 | * - ihandle package lock contention | |
475 | * - closing file descriptor(s) associated with ih | |
476 | * | |
477 | * Hance, we perform outside of the volume package lock in order to | |
478 | * reduce the probability of contention. | |
479 | */ | |
480 | IH_RELEASE(vnp->handle); | |
481 | #ifdef AFS_DEMAND_ATTACH_FS | |
482 | VOL_LOCK; | |
483 | #endif | |
484 | } | |
485 | ||
486 | #ifdef AFS_DEMAND_ATTACH_FS | |
487 | VnChangeState_r(vnp, VN_STATE_INVALID); | |
488 | #endif | |
489 | ||
490 | return vnp; | |
491 | } | |
492 | ||
493 | ||
494 | /** | |
495 | * lookup a vnode in the vnode cache hash table. | |
496 | * | |
497 | * @param[in] vp pointer to volume object | |
498 | * @param[in] vnodeId vnode id | |
499 | * | |
500 | * @pre VOL_LOCK held | |
501 | * | |
502 | * @post matching vnode object or NULL is returned | |
503 | * | |
504 | * @return vnode object pointer | |
505 | * @retval NULL no matching vnode object was found in the cache | |
506 | * | |
507 | * @internal vnode package internal use only | |
508 | * | |
509 | * @note this symbol is exported strictly for fssync debug protocol use | |
510 | */ | |
511 | Vnode * | |
512 | VLookupVnode(Volume * vp, VnodeId vnodeId) | |
513 | { | |
514 | Vnode * vnp; | |
515 | unsigned int newHash; | |
516 | ||
517 | newHash = VNODE_HASH(vp, vnodeId); | |
518 | for (vnp = VnodeHashTable[newHash]; | |
519 | (vnp && | |
520 | ((Vn_id(vnp) != vnodeId) || | |
521 | (Vn_volume(vnp) != vp) || | |
522 | (vp->cacheCheck != Vn_cacheCheck(vnp)))); | |
523 | vnp = vnp->hashNext); | |
524 | ||
525 | return vnp; | |
526 | } | |
527 | ||
528 | ||
529 | Vnode * | |
530 | VAllocVnode(Error * ec, Volume * vp, VnodeType type, VnodeId in_vnode, Unique in_unique) | |
531 | { | |
532 | Vnode *retVal; | |
533 | VOL_LOCK; | |
534 | retVal = VAllocVnode_r(ec, vp, type, in_vnode, in_unique); | |
535 | VOL_UNLOCK; | |
536 | return retVal; | |
537 | } | |
538 | ||
539 | /** | |
540 | * allocate a new vnode. | |
541 | * | |
542 | * @param[out] ec error code return | |
543 | * @param[in] vp volume object pointer | |
544 | * @param[in] type desired vnode type | |
545 | * @param[in] type desired vnode ID (optional) | |
546 | * @param[in] type desired vnode Unique (optional) | |
547 | * | |
548 | * @return vnode object pointer | |
549 | * | |
550 | * @pre VOL_LOCK held; | |
551 | * heavyweight ref held on vp | |
552 | * | |
553 | * @post vnode allocated and returned | |
554 | */ | |
555 | Vnode * | |
556 | VAllocVnode_r(Error * ec, Volume * vp, VnodeType type, VnodeId in_vnode, Unique in_unique) | |
557 | { | |
558 | Vnode *vnp; | |
559 | VnodeId vnodeNumber; | |
560 | int bitNumber; | |
561 | struct VnodeClassInfo *vcp; | |
562 | VnodeClass class; | |
563 | Unique unique; | |
564 | struct vnodeIndex *index; | |
565 | unsigned int offset; | |
566 | ||
567 | #ifdef AFS_DEMAND_ATTACH_FS | |
568 | VolState vol_state_save; | |
569 | #endif | |
570 | ||
571 | *ec = 0; | |
572 | ||
573 | #ifdef AFS_DEMAND_ATTACH_FS | |
574 | /* | |
575 | * once a volume has entered an error state, don't permit | |
576 | * further operations to proceed | |
577 | * -- tkeiser 11/21/2007 | |
578 | */ | |
579 | VWaitExclusiveState_r(vp); | |
580 | if (VIsErrorState(V_attachState(vp))) { | |
581 | /* XXX is VSALVAGING acceptable here? */ | |
582 | *ec = DAFS_VSALVAGE; | |
583 | return NULL; | |
584 | } | |
585 | #endif | |
586 | ||
587 | if (programType == fileServer && !V_inUse(vp)) { | |
588 | if (vp->specialStatus) { | |
589 | *ec = vp->specialStatus; | |
590 | } else { | |
591 | *ec = VOFFLINE; | |
592 | } | |
593 | return NULL; | |
594 | } | |
595 | class = vnodeTypeToClass(type); | |
596 | vcp = &VnodeClassInfo[class]; | |
597 | ||
598 | if (!VolumeWriteable(vp)) { | |
599 | *ec = (bit32) VREADONLY; | |
600 | return NULL; | |
601 | } | |
602 | ||
603 | if (vp->nextVnodeUnique > V_uniquifier(vp)) { | |
604 | VUpdateVolume_r(ec, vp, 0); | |
605 | if (*ec) | |
606 | return NULL; | |
607 | } | |
608 | ||
609 | if (programType == fileServer) { | |
610 | VAddToVolumeUpdateList_r(ec, vp); | |
611 | if (*ec) | |
612 | return NULL; | |
613 | } | |
614 | ||
615 | /* | |
616 | * If in_vnode and in_unique are specified, we are asked to | |
617 | * allocate a specifc vnode slot. Used by RW replication to | |
618 | * keep vnode IDs consistent with the master. | |
619 | */ | |
620 | ||
621 | if (!in_vnode) { | |
622 | int rollover = 0; | |
623 | ||
624 | unique = vp->nextVnodeUnique++; | |
625 | if (unique == 0) { | |
626 | rollover = 1; /* nextVnodeUnique rolled over */ | |
627 | vp->nextVnodeUnique = 2; /* 1 is reserved for the root vnode */ | |
628 | unique = vp->nextVnodeUnique++; | |
629 | } | |
630 | ||
631 | if (vp->nextVnodeUnique > V_uniquifier(vp) || rollover) { | |
632 | VUpdateVolume_r(ec, vp, 0); | |
633 | if (*ec) | |
634 | return NULL; | |
635 | } | |
636 | ||
637 | /* Find a slot in the bit map */ | |
638 | bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class], | |
639 | VOL_ALLOC_BITMAP_WAIT); | |
640 | ||
641 | if (*ec) | |
642 | return NULL; | |
643 | vnodeNumber = bitNumberToVnodeNumber(bitNumber, class); | |
644 | } else { | |
645 | index = &vp->vnodeIndex[class]; | |
646 | if (!in_unique) { | |
647 | *ec = VNOVNODE; | |
648 | return NULL; | |
649 | } | |
650 | /* Catch us up to where the master is */ | |
651 | if (in_unique > vp->nextVnodeUnique) | |
652 | vp->nextVnodeUnique = in_unique+1; | |
653 | ||
654 | if (vp->nextVnodeUnique > V_uniquifier(vp)) { | |
655 | VUpdateVolume_r(ec, vp, 0); | |
656 | if (*ec) | |
657 | return NULL; | |
658 | } | |
659 | ||
660 | unique = in_unique; | |
661 | bitNumber = vnodeIdToBitNumber(in_vnode); | |
662 | offset = bitNumber >> 3; | |
663 | ||
664 | /* Mark vnode in use. Grow bitmap if needed. */ | |
665 | if ((offset >= index->bitmapSize) | |
666 | || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7))) == 0)) | |
667 | VGrowBitmap(index); | |
668 | /* Should not happen */ | |
669 | if (*(index->bitmap + offset) & (1 << (bitNumber & 0x7))) { | |
670 | *ec = VNOVNODE; | |
671 | return NULL; | |
672 | } | |
673 | ||
674 | *(index->bitmap + offset) |= (1 << (bitNumber & 0x7)); | |
675 | vnodeNumber = in_vnode; | |
676 | } | |
677 | ||
678 | /* | |
679 | * DAFS: | |
680 | * at this point we should be assured that V_attachState(vp) is non-exclusive | |
681 | */ | |
682 | ||
683 | vnrehash: | |
684 | VNLog(2, 1, vnodeNumber, 0, 0, 0); | |
685 | /* Prepare to move it to the new hash chain */ | |
686 | vnp = VLookupVnode(vp, vnodeNumber); | |
687 | if (vnp) { | |
688 | /* slot already exists. May even not be in lruq (consider store file locking a file being deleted) | |
689 | * so we may have to wait for it below */ | |
690 | VNLog(3, 2, vnodeNumber, (intptr_t)vnp, 0, 0); | |
691 | ||
692 | VnCreateReservation_r(vnp); | |
693 | if (Vn_refcount(vnp) == 1) { | |
694 | /* we're the only user */ | |
695 | /* This won't block */ | |
696 | VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK); | |
697 | } else { | |
698 | #ifdef AFS_DEMAND_ATTACH_FS | |
699 | /* | |
700 | * DAFS: | |
701 | * vnode was cached, wait for any existing exclusive ops to finish. | |
702 | * once we have reacquired the lock, re-verify volume state. | |
703 | * | |
704 | * note: any vnode error state is related to the old vnode; disregard. | |
705 | */ | |
706 | VnWaitQuiescent_r(vnp); | |
707 | if (VIsErrorState(V_attachState(vp))) { | |
708 | VnUnlock(vnp, WRITE_LOCK); | |
709 | VnCancelReservation_r(vnp); | |
710 | *ec = DAFS_VSALVAGE; | |
711 | return NULL; | |
712 | } | |
713 | #endif | |
714 | ||
715 | /* other users present; follow locking hierarchy */ | |
716 | VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, MIGHT_DEADLOCK); | |
717 | ||
718 | /* | |
719 | * verify state of the world hasn't changed | |
720 | * | |
721 | * (technically, this should never happen because cachecheck | |
722 | * is only updated during a volume attach, which should not | |
723 | * happen when refs are held) | |
724 | */ | |
725 | if (Vn_volume(vnp)->cacheCheck != Vn_cacheCheck(vnp)) { | |
726 | VnUnlock(vnp, WRITE_LOCK); | |
727 | VnCancelReservation_r(vnp); | |
728 | goto vnrehash; | |
729 | } | |
730 | } | |
731 | ||
732 | /* sanity check: vnode should be blank if it was deleted. If it's | |
733 | * not blank, it is still in use somewhere; but the bitmap told us | |
734 | * this vnode number was free, so something is wrong. */ | |
735 | if (vnp->disk.type != vNull) { | |
736 | Error tmp; | |
737 | Log("VAllocVnode: addled bitmap or vnode object! (vol %" AFS_VOLID_FMT ", " | |
738 | "vnode %p, number %ld, type %ld)\n", afs_printable_VolumeId_lu(vp->hashid), vnp, | |
739 | (long)Vn_id(vnp), (long)vnp->disk.type); | |
740 | *ec = EIO; | |
741 | VFreeBitMapEntry_r(&tmp, vp, &vp->vnodeIndex[class], bitNumber, | |
742 | VOL_FREE_BITMAP_WAIT); | |
743 | VInvalidateVnode_r(vnp); | |
744 | VnUnlock(vnp, WRITE_LOCK); | |
745 | VnCancelReservation_r(vnp); | |
746 | #ifdef AFS_DEMAND_ATTACH_FS | |
747 | VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0); | |
748 | #else | |
749 | VForceOffline_r(vp, 0); | |
750 | #endif | |
751 | return NULL; | |
752 | } | |
753 | ||
754 | } else { | |
755 | /* no such vnode in the cache */ | |
756 | ||
757 | vnp = VGetFreeVnode_r(vcp, vp, vnodeNumber); | |
758 | ||
759 | /* This will never block (guaranteed by check in VGetFreeVnode_r() */ | |
760 | VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK); | |
761 | ||
762 | #ifdef AFS_DEMAND_ATTACH_FS | |
763 | VnChangeState_r(vnp, VN_STATE_ALLOC); | |
764 | #endif | |
765 | ||
766 | /* Sanity check: is this vnode really not in use? */ | |
767 | { | |
768 | afs_sfsize_t size; | |
769 | IHandle_t *ihP = vp->vnodeIndex[class].handle; | |
770 | FdHandle_t *fdP; | |
771 | afs_foff_t off = vnodeIndexOffset(vcp, vnodeNumber); | |
772 | Error tmp; | |
773 | ||
774 | /* XXX we have a potential race here if two threads | |
775 | * allocate new vnodes at the same time, and they | |
776 | * both decide it's time to extend the index | |
777 | * file size... | |
778 | */ | |
779 | #ifdef AFS_DEMAND_ATTACH_FS | |
780 | /* | |
781 | * this race has been eliminated for the DAFS case | |
782 | * using exclusive state VOL_STATE_VNODE_ALLOC | |
783 | * | |
784 | * if this becomes a bottleneck, there are ways to | |
785 | * improve parallelism for this code path | |
786 | * -- tkeiser 11/28/2007 | |
787 | */ | |
788 | VCreateReservation_r(vp); | |
789 | VWaitExclusiveState_r(vp); | |
790 | vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_ALLOC); | |
791 | #endif | |
792 | ||
793 | VOL_UNLOCK; | |
794 | fdP = IH_OPEN(ihP); | |
795 | if (fdP == NULL) { | |
796 | Log("VAllocVnode: can't open index file!\n"); | |
797 | *ec = ENOENT; | |
798 | goto error_encountered; | |
799 | } | |
800 | if ((size = FDH_SIZE(fdP)) < 0) { | |
801 | Log("VAllocVnode: can't stat index file!\n"); | |
802 | *ec = EIO; | |
803 | goto error_encountered; | |
804 | } | |
805 | if (off + vcp->diskSize <= size) { | |
806 | if (FDH_PREAD(fdP, &vnp->disk, vcp->diskSize, off) != vcp->diskSize) { | |
807 | Log("VAllocVnode: can't read index file!\n"); | |
808 | *ec = EIO; | |
809 | goto error_encountered; | |
810 | } | |
811 | if (vnp->disk.type != vNull) { | |
812 | Log("VAllocVnode: addled bitmap or index!\n"); | |
813 | *ec = EIO; | |
814 | goto error_encountered; | |
815 | } | |
816 | } else { | |
817 | /* growing file - grow in a reasonable increment */ | |
818 | char *buf = malloc(16 * 1024); | |
819 | if (!buf) { | |
820 | Log("VAllocVnode: can't grow vnode index: out of memory\n"); | |
821 | *ec = ENOMEM; | |
822 | goto error_encountered; | |
823 | } | |
824 | memset(buf, 0, 16 * 1024); | |
825 | if ((FDH_PWRITE(fdP, buf, 16 * 1024, off)) != 16 * 1024) { | |
826 | Log("VAllocVnode: can't grow vnode index: write failed\n"); | |
827 | *ec = EIO; | |
828 | free(buf); | |
829 | goto error_encountered; | |
830 | } | |
831 | free(buf); | |
832 | } | |
833 | FDH_CLOSE(fdP); | |
834 | VOL_LOCK; | |
835 | ||
836 | #ifdef AFS_DEMAND_ATTACH_FS | |
837 | VChangeState_r(vp, vol_state_save); | |
838 | VCancelReservation_r(vp); | |
839 | #endif | |
840 | goto sane; | |
841 | ||
842 | ||
843 | error_encountered: | |
844 | /* | |
845 | * close the file handle | |
846 | * acquire VOL_LOCK | |
847 | * invalidate the vnode | |
848 | * free up the bitmap entry (although salvager should take care of it) | |
849 | * salvage the volume | |
850 | * drop vnode lock and refs | |
851 | */ | |
852 | if (fdP) | |
853 | FDH_CLOSE(fdP); | |
854 | VOL_LOCK; | |
855 | VFreeBitMapEntry_r(&tmp, vp, &vp->vnodeIndex[class], bitNumber, 0 /*flags*/); | |
856 | VInvalidateVnode_r(vnp); | |
857 | VnUnlock(vnp, WRITE_LOCK); | |
858 | VnCancelReservation_r(vnp); | |
859 | #ifdef AFS_DEMAND_ATTACH_FS | |
860 | VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0); | |
861 | VCancelReservation_r(vp); | |
862 | #else | |
863 | VForceOffline_r(vp, 0); | |
864 | #endif | |
865 | return NULL; | |
866 | } | |
867 | sane: | |
868 | VNLog(4, 2, vnodeNumber, (intptr_t)vnp, 0, 0); | |
869 | #ifndef AFS_DEMAND_ATTACH_FS | |
870 | AddToVnHash(vnp); | |
871 | #endif | |
872 | } | |
873 | ||
874 | VNLog(5, 1, (intptr_t)vnp, 0, 0, 0); | |
875 | memset(&vnp->disk, 0, sizeof(vnp->disk)); | |
876 | vnp->changed_newTime = 0; /* set this bit when vnode is updated */ | |
877 | vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */ | |
878 | vnp->delete = 0; | |
879 | vnp->disk.vnodeMagic = vcp->magic; | |
880 | vnp->disk.type = type; | |
881 | vnp->disk.uniquifier = unique; | |
882 | vnp->handle = NULL; | |
883 | vcp->allocs++; | |
884 | V_filecount(vp)++; | |
885 | #ifdef AFS_DEMAND_ATTACH_FS | |
886 | VnChangeState_r(vnp, VN_STATE_EXCLUSIVE); | |
887 | #endif | |
888 | return vnp; | |
889 | } | |
890 | ||
891 | /** | |
892 | * load a vnode from disk. | |
893 | * | |
894 | * @param[out] ec client error code return | |
895 | * @param[in] vp volume object pointer | |
896 | * @param[in] vnp vnode object pointer | |
897 | * @param[in] vcp vnode class info object pointer | |
898 | * @param[in] class vnode class enumeration | |
899 | * | |
900 | * @pre vnode is registered in appropriate data structures; | |
901 | * caller holds a ref on vnode; VOL_LOCK is held | |
902 | * | |
903 | * @post vnode data is loaded from disk. | |
904 | * vnode state is set to VN_STATE_ONLINE. | |
905 | * on failure, vnode is invalidated. | |
906 | * | |
907 | * @internal vnode package internal use only | |
908 | */ | |
909 | static void | |
910 | VnLoad(Error * ec, Volume * vp, Vnode * vnp, | |
911 | struct VnodeClassInfo * vcp, VnodeClass class) | |
912 | { | |
913 | /* vnode not cached */ | |
914 | Error error; | |
915 | int dosalv = 1; | |
916 | ssize_t nBytes; | |
917 | IHandle_t *ihP = vp->vnodeIndex[class].handle; | |
918 | FdHandle_t *fdP; | |
919 | afs_ino_str_t stmp; | |
920 | ||
921 | *ec = 0; | |
922 | vcp->reads++; | |
923 | ||
924 | #ifdef AFS_DEMAND_ATTACH_FS | |
925 | VnChangeState_r(vnp, VN_STATE_LOAD); | |
926 | #endif | |
927 | ||
928 | /* This will never block */ | |
929 | VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK); | |
930 | ||
931 | VOL_UNLOCK; | |
932 | fdP = IH_OPEN(ihP); | |
933 | if (fdP == NULL) { | |
934 | Log("VnLoad: can't open index dev=%u, i=%s\n", vp->device, | |
935 | PrintInode(stmp, vp->vnodeIndex[class].handle->ih_ino)); | |
936 | *ec = VIO; | |
937 | goto error_encountered_nolock; | |
938 | } else if ((nBytes = FDH_PREAD(fdP, (char *)&vnp->disk, vcp->diskSize, vnodeIndexOffset(vcp, Vn_id(vnp)))) | |
939 | != vcp->diskSize) { | |
940 | /* Don't take volume off line if the inumber is out of range | |
941 | * or the inode table is full. */ | |
942 | if (nBytes == BAD_IGET) { | |
943 | Log("VnLoad: bad inumber %s\n", | |
944 | PrintInode(stmp, vp->vnodeIndex[class].handle->ih_ino)); | |
945 | *ec = VIO; | |
946 | dosalv = 0; | |
947 | } else if (nBytes == -1 && errno == EIO) { | |
948 | /* disk error; salvage */ | |
949 | Log("VnLoad: Couldn't read vnode %u, volume %" AFS_VOLID_FMT " (%s); volume needs salvage\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp)); | |
950 | } else { | |
951 | /* vnode is not allocated */ | |
952 | if (GetLogLevel() >= 5) | |
953 | Log("VnLoad: Couldn't read vnode %u, volume %" AFS_VOLID_FMT " (%s); read %d bytes, errno %d\n", | |
954 | Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp), (int)nBytes, errno); | |
955 | *ec = VNOVNODE; | |
956 | dosalv = 0; | |
957 | } | |
958 | goto error_encountered_nolock; | |
959 | } | |
960 | FDH_CLOSE(fdP); | |
961 | VOL_LOCK; | |
962 | ||
963 | /* Quick check to see that the data is reasonable */ | |
964 | if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) { | |
965 | if (vnp->disk.type == vNull) { | |
966 | *ec = VNOVNODE; | |
967 | dosalv = 0; | |
968 | } else { | |
969 | struct vnodeIndex *index = &vp->vnodeIndex[class]; | |
970 | unsigned int bitNumber = vnodeIdToBitNumber(Vn_id(vnp)); | |
971 | unsigned int offset = bitNumber >> 3; | |
972 | ||
973 | #ifdef AFS_DEMAND_ATTACH_FS | |
974 | /* Make sure the volume bitmap isn't getting updated while we are | |
975 | * checking it */ | |
976 | VWaitExclusiveState_r(vp); | |
977 | #endif | |
978 | ||
979 | /* Test to see if vnode number is valid. */ | |
980 | if ((offset >= index->bitmapSize) | |
981 | || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7))) | |
982 | == 0)) { | |
983 | Log("VnLoad: Request for unallocated vnode %u, volume %" AFS_VOLID_FMT " (%s) denied.\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp)); | |
984 | *ec = VNOVNODE; | |
985 | dosalv = 0; | |
986 | } else { | |
987 | Log("VnLoad: Bad magic number, vnode %u, volume %" AFS_VOLID_FMT " (%s); volume needs salvage\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp)); | |
988 | } | |
989 | } | |
990 | goto error_encountered; | |
991 | } | |
992 | ||
993 | IH_INIT(vnp->handle, V_device(vp), afs_printable_VolumeId_lu(V_parentId(vp)), VN_GET_INO(vnp)); | |
994 | VnUnlock(vnp, WRITE_LOCK); | |
995 | #ifdef AFS_DEMAND_ATTACH_FS | |
996 | VnChangeState_r(vnp, VN_STATE_ONLINE); | |
997 | #endif | |
998 | return; | |
999 | ||
1000 | ||
1001 | error_encountered_nolock: | |
1002 | if (fdP) { | |
1003 | FDH_REALLYCLOSE(fdP); | |
1004 | } | |
1005 | VOL_LOCK; | |
1006 | ||
1007 | error_encountered: | |
1008 | if (dosalv) { | |
1009 | #ifdef AFS_DEMAND_ATTACH_FS | |
1010 | VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0); | |
1011 | #else | |
1012 | VForceOffline_r(vp, 0); | |
1013 | error = VSALVAGE; | |
1014 | #endif | |
1015 | if (!*ec) | |
1016 | *ec = error; | |
1017 | } | |
1018 | ||
1019 | VInvalidateVnode_r(vnp); | |
1020 | VnUnlock(vnp, WRITE_LOCK); | |
1021 | } | |
1022 | ||
1023 | /** | |
1024 | * store a vnode to disk. | |
1025 | * | |
1026 | * @param[out] ec error code output | |
1027 | * @param[in] vp volume object pointer | |
1028 | * @param[in] vnp vnode object pointer | |
1029 | * @param[in] vcp vnode class info object pointer | |
1030 | * @param[in] class vnode class enumeration | |
1031 | * | |
1032 | * @pre VOL_LOCK held. | |
1033 | * caller holds refs to volume and vnode. | |
1034 | * DAFS: caller is responsible for performing state sanity checks. | |
1035 | * | |
1036 | * @post vnode state is stored to disk. | |
1037 | * | |
1038 | * @internal vnode package internal use only | |
1039 | */ | |
1040 | static void | |
1041 | VnStore(Error * ec, Volume * vp, Vnode * vnp, | |
1042 | struct VnodeClassInfo * vcp, VnodeClass class) | |
1043 | { | |
1044 | ssize_t nBytes; | |
1045 | afs_foff_t offset; | |
1046 | IHandle_t *ihP = vp->vnodeIndex[class].handle; | |
1047 | FdHandle_t *fdP; | |
1048 | afs_ino_str_t stmp; | |
1049 | #ifdef AFS_DEMAND_ATTACH_FS | |
1050 | VnState vn_state_save; | |
1051 | #endif | |
1052 | ||
1053 | *ec = 0; | |
1054 | ||
1055 | #ifdef AFS_DEMAND_ATTACH_FS | |
1056 | vn_state_save = VnChangeState_r(vnp, VN_STATE_STORE); | |
1057 | #endif | |
1058 | ||
1059 | offset = vnodeIndexOffset(vcp, Vn_id(vnp)); | |
1060 | VOL_UNLOCK; | |
1061 | fdP = IH_OPEN(ihP); | |
1062 | if (fdP == NULL) { | |
1063 | Log("VnStore: can't open index file!\n"); | |
1064 | goto error_encountered; | |
1065 | } | |
1066 | nBytes = FDH_PWRITE(fdP, &vnp->disk, vcp->diskSize, offset); | |
1067 | if (nBytes != vcp->diskSize) { | |
1068 | /* Don't force volume offline if the inumber is out of | |
1069 | * range or the inode table is full. | |
1070 | */ | |
1071 | FDH_REALLYCLOSE(fdP); | |
1072 | if (nBytes == BAD_IGET) { | |
1073 | Log("VnStore: bad inumber %s\n", | |
1074 | PrintInode(stmp, | |
1075 | vp->vnodeIndex[class].handle->ih_ino)); | |
1076 | *ec = VIO; | |
1077 | VOL_LOCK; | |
1078 | #ifdef AFS_DEMAND_ATTACH_FS | |
1079 | VnChangeState_r(vnp, VN_STATE_ERROR); | |
1080 | #endif | |
1081 | } else { | |
1082 | Log("VnStore: Couldn't write vnode %u, volume %" AFS_VOLID_FMT " (%s) (error %d)\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(Vn_volume(vnp))), V_name(Vn_volume(vnp)), (int)nBytes); | |
1083 | #ifdef AFS_DEMAND_ATTACH_FS | |
1084 | goto error_encountered; | |
1085 | #else | |
1086 | VOL_LOCK; | |
1087 | VForceOffline_r(vp, 0); | |
1088 | *ec = VSALVAGE; | |
1089 | #endif | |
1090 | } | |
1091 | return; | |
1092 | } else { | |
1093 | FDH_CLOSE(fdP); | |
1094 | } | |
1095 | ||
1096 | VOL_LOCK; | |
1097 | #ifdef AFS_DEMAND_ATTACH_FS | |
1098 | VnChangeState_r(vnp, vn_state_save); | |
1099 | #endif | |
1100 | return; | |
1101 | ||
1102 | error_encountered: | |
1103 | #ifdef AFS_DEMAND_ATTACH_FS | |
1104 | /* XXX instead of dumping core, let's try to request a salvage | |
1105 | * and just fail the putvnode */ | |
1106 | if (fdP) | |
1107 | FDH_CLOSE(fdP); | |
1108 | VOL_LOCK; | |
1109 | VnChangeState_r(vnp, VN_STATE_ERROR); | |
1110 | VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0); | |
1111 | #else | |
1112 | opr_abort(); | |
1113 | #endif | |
1114 | } | |
1115 | ||
1116 | /** | |
1117 | * get a handle to a vnode object. | |
1118 | * | |
1119 | * @param[out] ec error code | |
1120 | * @param[in] vp volume object | |
1121 | * @param[in] vnodeNumber vnode id | |
1122 | * @param[in] locktype type of lock to acquire | |
1123 | * | |
1124 | * @return vnode object pointer | |
1125 | * | |
1126 | * @see VGetVnode_r | |
1127 | */ | |
1128 | Vnode * | |
1129 | VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype) | |
1130 | { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */ | |
1131 | Vnode *retVal; | |
1132 | VOL_LOCK; | |
1133 | retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype); | |
1134 | VOL_UNLOCK; | |
1135 | return retVal; | |
1136 | } | |
1137 | ||
1138 | /** | |
1139 | * get a handle to a vnode object. | |
1140 | * | |
1141 | * @param[out] ec error code | |
1142 | * @param[in] vp volume object | |
1143 | * @param[in] vnodeNumber vnode id | |
1144 | * @param[in] locktype type of lock to acquire | |
1145 | * | |
1146 | * @return vnode object pointer | |
1147 | * | |
1148 | * @internal vnode package internal use only | |
1149 | * | |
1150 | * @pre VOL_LOCK held. | |
1151 | * heavyweight ref held on volume object. | |
1152 | */ | |
1153 | Vnode * | |
1154 | VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype) | |
1155 | { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */ | |
1156 | Vnode *vnp; | |
1157 | VnodeClass class; | |
1158 | struct VnodeClassInfo *vcp; | |
1159 | ||
1160 | *ec = 0; | |
1161 | ||
1162 | if (vnodeNumber == 0) { | |
1163 | *ec = VNOVNODE; | |
1164 | return NULL; | |
1165 | } | |
1166 | ||
1167 | VNLog(100, 1, vnodeNumber, 0, 0, 0); | |
1168 | ||
1169 | #ifdef AFS_DEMAND_ATTACH_FS | |
1170 | /* | |
1171 | * once a volume has entered an error state, don't permit | |
1172 | * further operations to proceed | |
1173 | * -- tkeiser 11/21/2007 | |
1174 | */ | |
1175 | VWaitExclusiveState_r(vp); | |
1176 | if (VIsErrorState(V_attachState(vp))) { | |
1177 | /* XXX is VSALVAGING acceptable here? */ | |
1178 | *ec = VSALVAGING; | |
1179 | return NULL; | |
1180 | } | |
1181 | #endif | |
1182 | ||
1183 | if (programType == fileServer && !V_inUse(vp)) { | |
1184 | *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE); | |
1185 | ||
1186 | /* If the volume is VBUSY (being cloned or dumped) and this is | |
1187 | * a READ operation, then don't fail. | |
1188 | */ | |
1189 | if ((*ec != VBUSY) || (locktype != READ_LOCK)) { | |
1190 | return NULL; | |
1191 | } | |
1192 | *ec = 0; | |
1193 | } | |
1194 | class = vnodeIdToClass(vnodeNumber); | |
1195 | vcp = &VnodeClassInfo[class]; | |
1196 | if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) { | |
1197 | *ec = (bit32) VREADONLY; | |
1198 | return NULL; | |
1199 | } | |
1200 | ||
1201 | if (locktype == WRITE_LOCK && programType == fileServer) { | |
1202 | VAddToVolumeUpdateList_r(ec, vp); | |
1203 | if (*ec) { | |
1204 | return NULL; | |
1205 | } | |
1206 | } | |
1207 | ||
1208 | vcp->gets++; | |
1209 | ||
1210 | /* See whether the vnode is in the cache. */ | |
1211 | vnp = VLookupVnode(vp, vnodeNumber); | |
1212 | if (vnp) { | |
1213 | /* vnode is in cache */ | |
1214 | ||
1215 | VNLog(101, 2, vnodeNumber, (intptr_t)vnp, 0, 0); | |
1216 | VnCreateReservation_r(vnp); | |
1217 | ||
1218 | #ifdef AFS_DEMAND_ATTACH_FS | |
1219 | /* | |
1220 | * this is the one DAFS case where we may run into contention. | |
1221 | * here's the basic control flow: | |
1222 | * | |
1223 | * if locktype is READ_LOCK: | |
1224 | * wait until vnode is not exclusive | |
1225 | * set to VN_STATE_READ | |
1226 | * increment read count | |
1227 | * done | |
1228 | * else | |
1229 | * wait until vnode is quiescent | |
1230 | * set to VN_STATE_EXCLUSIVE | |
1231 | * done | |
1232 | */ | |
1233 | if (locktype == READ_LOCK) { | |
1234 | VnWaitExclusiveState_r(vnp); | |
1235 | } else { | |
1236 | VnWaitQuiescent_r(vnp); | |
1237 | } | |
1238 | ||
1239 | if (VnIsErrorState(Vn_state(vnp))) { | |
1240 | VnCancelReservation_r(vnp); | |
1241 | *ec = VSALVAGE; | |
1242 | return NULL; | |
1243 | } | |
1244 | #endif /* AFS_DEMAND_ATTACH_FS */ | |
1245 | } else { | |
1246 | /* vnode not cached */ | |
1247 | ||
1248 | /* Not in cache; tentatively grab most distantly used one from the LRU | |
1249 | * chain */ | |
1250 | vcp->reads++; | |
1251 | vnp = VGetFreeVnode_r(vcp, vp, vnodeNumber); | |
1252 | ||
1253 | /* Initialize */ | |
1254 | vnp->changed_newTime = vnp->changed_oldTime = 0; | |
1255 | vnp->delete = 0; | |
1256 | ||
1257 | /* | |
1258 | * XXX for non-DAFS, there is a serious | |
1259 | * race condition here: | |
1260 | * | |
1261 | * two threads can race to load a vnode. the net | |
1262 | * result is two struct Vnodes can be allocated | |
1263 | * and hashed, which point to the same underlying | |
1264 | * disk data store. conflicting vnode locks can | |
1265 | * thus be held concurrently. | |
1266 | * | |
1267 | * for non-DAFS to be safe, VOL_LOCK really shouldn't | |
1268 | * be dropped in VnLoad. Of course, this would likely | |
1269 | * lead to an unacceptable slow-down. | |
1270 | */ | |
1271 | ||
1272 | VnLoad(ec, vp, vnp, vcp, class); | |
1273 | if (*ec) { | |
1274 | VnCancelReservation_r(vnp); | |
1275 | return NULL; | |
1276 | } | |
1277 | #ifndef AFS_DEMAND_ATTACH_FS | |
1278 | AddToVnHash(vnp); | |
1279 | #endif | |
1280 | /* | |
1281 | * DAFS: | |
1282 | * there is no possibility for contention. we "own" this vnode. | |
1283 | */ | |
1284 | } | |
1285 | ||
1286 | /* | |
1287 | * DAFS: | |
1288 | * it is imperative that nothing drop vol lock between here | |
1289 | * and the VnBeginRead/VnChangeState stanza below | |
1290 | */ | |
1291 | ||
1292 | VnLock(vnp, locktype, VOL_LOCK_HELD, MIGHT_DEADLOCK); | |
1293 | ||
1294 | /* Check that the vnode hasn't been removed while we were obtaining | |
1295 | * the lock */ | |
1296 | VNLog(102, 2, vnodeNumber, (intptr_t) vnp, 0, 0); | |
1297 | if ((vnp->disk.type == vNull) || (Vn_cacheCheck(vnp) == 0)) { | |
1298 | VnUnlock(vnp, locktype); | |
1299 | VnCancelReservation_r(vnp); | |
1300 | *ec = VNOVNODE; | |
1301 | /* vnode is labelled correctly by now, so we don't have to invalidate it */ | |
1302 | return NULL; | |
1303 | } | |
1304 | ||
1305 | #ifdef AFS_DEMAND_ATTACH_FS | |
1306 | if (locktype == READ_LOCK) { | |
1307 | VnBeginRead_r(vnp); | |
1308 | } else { | |
1309 | VnChangeState_r(vnp, VN_STATE_EXCLUSIVE); | |
1310 | } | |
1311 | #endif | |
1312 | ||
1313 | if (programType == fileServer) | |
1314 | VBumpVolumeUsage_r(Vn_volume(vnp)); /* Hack; don't know where it should be | |
1315 | * called from. Maybe VGetVolume */ | |
1316 | return vnp; | |
1317 | } | |
1318 | ||
1319 | ||
1320 | int TrustVnodeCacheEntry = 1; | |
1321 | /* This variable is bogus--when it's set to 0, the hash chains fill | |
1322 | up with multiple versions of the same vnode. Should fix this!! */ | |
1323 | void | |
1324 | VPutVnode(Error * ec, Vnode * vnp) | |
1325 | { | |
1326 | VOL_LOCK; | |
1327 | VPutVnode_r(ec, vnp); | |
1328 | VOL_UNLOCK; | |
1329 | } | |
1330 | ||
1331 | /** | |
1332 | * put back a handle to a vnode object. | |
1333 | * | |
1334 | * @param[out] ec client error code | |
1335 | * @param[in] vnp vnode object pointer | |
1336 | * | |
1337 | * @pre VOL_LOCK held. | |
1338 | * ref held on vnode. | |
1339 | * | |
1340 | * @post ref dropped on vnode. | |
1341 | * if vnode was modified or deleted, it is written out to disk | |
1342 | * (assuming a write lock was held). | |
1343 | * | |
1344 | * @internal volume package internal use only | |
1345 | */ | |
1346 | void | |
1347 | VPutVnode_r(Error * ec, Vnode * vnp) | |
1348 | { | |
1349 | int writeLocked; | |
1350 | VnodeClass class; | |
1351 | struct VnodeClassInfo *vcp; | |
1352 | ||
1353 | *ec = 0; | |
1354 | opr_Assert(Vn_refcount(vnp) != 0); | |
1355 | class = vnodeIdToClass(Vn_id(vnp)); | |
1356 | vcp = &VnodeClassInfo[class]; | |
1357 | opr_Assert(vnp->disk.vnodeMagic == vcp->magic); | |
1358 | VNLog(200, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0); | |
1359 | ||
1360 | #ifdef AFS_DEMAND_ATTACH_FS | |
1361 | writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE); | |
1362 | #else | |
1363 | writeLocked = WriteLocked(&vnp->lock); | |
1364 | #endif | |
1365 | ||
1366 | if (writeLocked) { | |
1367 | /* sanity checks */ | |
1368 | #ifdef AFS_PTHREAD_ENV | |
1369 | pthread_t thisProcess = pthread_self(); | |
1370 | #else /* AFS_PTHREAD_ENV */ | |
1371 | PROCESS thisProcess; | |
1372 | LWP_CurrentProcess(&thisProcess); | |
1373 | #endif /* AFS_PTHREAD_ENV */ | |
1374 | VNLog(201, 2, (intptr_t) vnp, | |
1375 | ((vnp->changed_newTime) << 1) | ((vnp-> | |
1376 | changed_oldTime) << 1) | vnp-> | |
1377 | delete, 0, 0); | |
1378 | if (thisProcess != vnp->writer) | |
1379 | Abort("VPutVnode: Vnode at %"AFS_PTR_FMT" locked by another process!\n", | |
1380 | vnp); | |
1381 | ||
1382 | ||
1383 | if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) { | |
1384 | Volume *vp = Vn_volume(vnp); | |
1385 | afs_uint32 now = FT_ApproxTime(); | |
1386 | opr_Assert(Vn_cacheCheck(vnp) == vp->cacheCheck); | |
1387 | ||
1388 | if (vnp->delete) { | |
1389 | /* No longer any directory entries for this vnode. Free the Vnode */ | |
1390 | memset(&vnp->disk, 0, sizeof(vnp->disk)); | |
1391 | /* delete flag turned off further down */ | |
1392 | VNLog(202, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0); | |
1393 | } else if (vnp->changed_newTime) { | |
1394 | vnp->disk.serverModifyTime = now; | |
1395 | } | |
1396 | if (vnp->changed_newTime) | |
1397 | { | |
1398 | V_updateDate(vp) = vp->updateTime = now; | |
1399 | if(V_volUpdateCounter(vp)< UINT_MAX) | |
1400 | V_volUpdateCounter(vp)++; | |
1401 | } | |
1402 | ||
1403 | /* The vnode has been changed. Write it out to disk */ | |
1404 | if (!V_inUse(vp)) { | |
1405 | #ifdef AFS_DEMAND_ATTACH_FS | |
1406 | VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0); | |
1407 | #else | |
1408 | opr_Assert(V_needsSalvaged(vp)); | |
1409 | *ec = VSALVAGE; | |
1410 | #endif | |
1411 | } else { | |
1412 | VnStore(ec, vp, vnp, vcp, class); | |
1413 | ||
1414 | /* If the vnode is to be deleted, and we wrote the vnode out, | |
1415 | * free its bitmap entry. Do after the vnode is written so we | |
1416 | * don't allocate from bitmap before the vnode is written | |
1417 | * (doing so could cause a "addled bitmap" message). | |
1418 | */ | |
1419 | if (vnp->delete && !*ec) { | |
1420 | if (V_filecount(Vn_volume(vnp))-- < 1) | |
1421 | V_filecount(Vn_volume(vnp)) = 0; | |
1422 | VFreeBitMapEntry_r(ec, vp, &vp->vnodeIndex[class], | |
1423 | vnodeIdToBitNumber(Vn_id(vnp)), | |
1424 | VOL_FREE_BITMAP_WAIT); | |
1425 | } | |
1426 | } | |
1427 | vcp->writes++; | |
1428 | vnp->changed_newTime = vnp->changed_oldTime = 0; | |
1429 | } | |
1430 | #ifdef AFS_DEMAND_ATTACH_FS | |
1431 | VnChangeState_r(vnp, VN_STATE_ONLINE); | |
1432 | #endif | |
1433 | } else { /* Not write locked */ | |
1434 | if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete) | |
1435 | Abort | |
1436 | ("VPutVnode: Change or delete flag for vnode " | |
1437 | "%"AFS_PTR_FMT" is set but vnode is not write locked!\n", | |
1438 | vnp); | |
1439 | #ifdef AFS_DEMAND_ATTACH_FS | |
1440 | VnEndRead_r(vnp); | |
1441 | #endif | |
1442 | } | |
1443 | ||
1444 | /* Do not look at disk portion of vnode after this point; it may | |
1445 | * have been deleted above */ | |
1446 | vnp->delete = 0; | |
1447 | VnUnlock(vnp, ((writeLocked) ? WRITE_LOCK : READ_LOCK)); | |
1448 | VnCancelReservation_r(vnp); | |
1449 | } | |
1450 | ||
1451 | /* | |
1452 | * Make an attempt to convert a vnode lock from write to read. | |
1453 | * Do nothing if the vnode isn't write locked or the vnode has | |
1454 | * been deleted. | |
1455 | */ | |
1456 | int | |
1457 | VVnodeWriteToRead(Error * ec, Vnode * vnp) | |
1458 | { | |
1459 | int retVal; | |
1460 | VOL_LOCK; | |
1461 | retVal = VVnodeWriteToRead_r(ec, vnp); | |
1462 | VOL_UNLOCK; | |
1463 | return retVal; | |
1464 | } | |
1465 | ||
1466 | /** | |
1467 | * convert vnode handle from mutually exclusive to shared access. | |
1468 | * | |
1469 | * @param[out] ec client error code | |
1470 | * @param[in] vnp vnode object pointer | |
1471 | * | |
1472 | * @return unspecified use (see out argument 'ec' for error code return) | |
1473 | * | |
1474 | * @pre VOL_LOCK held. | |
1475 | * ref held on vnode. | |
1476 | * write lock held on vnode. | |
1477 | * | |
1478 | * @post read lock held on vnode. | |
1479 | * if vnode was modified, it has been written to disk. | |
1480 | * | |
1481 | * @internal volume package internal use only | |
1482 | */ | |
1483 | int | |
1484 | VVnodeWriteToRead_r(Error * ec, Vnode * vnp) | |
1485 | { | |
1486 | int writeLocked; | |
1487 | VnodeClass class; | |
1488 | struct VnodeClassInfo *vcp; | |
1489 | #ifdef AFS_PTHREAD_ENV | |
1490 | pthread_t thisProcess; | |
1491 | #else /* AFS_PTHREAD_ENV */ | |
1492 | PROCESS thisProcess; | |
1493 | #endif /* AFS_PTHREAD_ENV */ | |
1494 | ||
1495 | *ec = 0; | |
1496 | opr_Assert(Vn_refcount(vnp) != 0); | |
1497 | class = vnodeIdToClass(Vn_id(vnp)); | |
1498 | vcp = &VnodeClassInfo[class]; | |
1499 | opr_Assert(vnp->disk.vnodeMagic == vcp->magic); | |
1500 | VNLog(300, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0); | |
1501 | ||
1502 | #ifdef AFS_DEMAND_ATTACH_FS | |
1503 | writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE); | |
1504 | #else | |
1505 | writeLocked = WriteLocked(&vnp->lock); | |
1506 | #endif | |
1507 | if (!writeLocked) { | |
1508 | return 0; | |
1509 | } | |
1510 | ||
1511 | ||
1512 | VNLog(301, 2, (intptr_t) vnp, | |
1513 | ((vnp->changed_newTime) << 1) | ((vnp-> | |
1514 | changed_oldTime) << 1) | vnp-> | |
1515 | delete, 0, 0); | |
1516 | ||
1517 | /* sanity checks */ | |
1518 | #ifdef AFS_PTHREAD_ENV | |
1519 | thisProcess = pthread_self(); | |
1520 | #else /* AFS_PTHREAD_ENV */ | |
1521 | LWP_CurrentProcess(&thisProcess); | |
1522 | #endif /* AFS_PTHREAD_ENV */ | |
1523 | if (thisProcess != vnp->writer) | |
1524 | Abort("VPutVnode: Vnode at %"AFS_PTR_FMT | |
1525 | " locked by another process!\n", vnp); | |
1526 | ||
1527 | if (vnp->delete) { | |
1528 | return 0; | |
1529 | } | |
1530 | if (vnp->changed_oldTime || vnp->changed_newTime) { | |
1531 | Volume *vp = Vn_volume(vnp); | |
1532 | afs_uint32 now = FT_ApproxTime(); | |
1533 | opr_Assert(Vn_cacheCheck(vnp) == vp->cacheCheck); | |
1534 | if (vnp->changed_newTime) | |
1535 | vnp->disk.serverModifyTime = now; | |
1536 | if (vnp->changed_newTime) | |
1537 | V_updateDate(vp) = vp->updateTime = now; | |
1538 | ||
1539 | /* The inode has been changed. Write it out to disk */ | |
1540 | if (!V_inUse(vp)) { | |
1541 | #ifdef AFS_DEMAND_ATTACH_FS | |
1542 | VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0); | |
1543 | #else | |
1544 | opr_Assert(V_needsSalvaged(vp)); | |
1545 | *ec = VSALVAGE; | |
1546 | #endif | |
1547 | } else { | |
1548 | VnStore(ec, vp, vnp, vcp, class); | |
1549 | } | |
1550 | vcp->writes++; | |
1551 | vnp->changed_newTime = vnp->changed_oldTime = 0; | |
1552 | } | |
1553 | ||
1554 | vnp->writer = 0; | |
1555 | #ifdef AFS_DEMAND_ATTACH_FS | |
1556 | VnChangeState_r(vnp, VN_STATE_ONLINE); | |
1557 | VnBeginRead_r(vnp); | |
1558 | #else | |
1559 | ConvertWriteToReadLock(&vnp->lock); | |
1560 | #endif | |
1561 | return 0; | |
1562 | } | |
1563 | ||
1564 | /** | |
1565 | * initial size of ihandle pointer vector. | |
1566 | * | |
1567 | * @see VInvalidateVnodesByVolume_r | |
1568 | */ | |
1569 | #define IH_VEC_BASE_SIZE 256 | |
1570 | ||
1571 | /** | |
1572 | * increment amount for growing ihandle pointer vector. | |
1573 | * | |
1574 | * @see VInvalidateVnodesByVolume_r | |
1575 | */ | |
1576 | #define IH_VEC_INCREMENT 256 | |
1577 | ||
1578 | /** | |
1579 | * Compile list of ihandles to be released/reallyclosed at a later time. | |
1580 | * | |
1581 | * @param[in] vp volume object pointer | |
1582 | * @param[out] vec_out vector of ihandle pointers to be released/reallyclosed | |
1583 | * @param[out] vec_len_out number of valid elements in ihandle vector | |
1584 | * | |
1585 | * @pre - VOL_LOCK is held | |
1586 | * - volume is in appropriate exclusive state (e.g. VOL_STATE_VNODE_CLOSE, | |
1587 | * VOL_STATE_VNODE_RELEASE) | |
1588 | * | |
1589 | * @post - all vnodes on VVn list are invalidated | |
1590 | * - ih_vec is populated with all valid ihandles | |
1591 | * | |
1592 | * @return operation status | |
1593 | * @retval 0 success | |
1594 | * @retval ENOMEM out of memory | |
1595 | * | |
1596 | * @todo we should handle out of memory conditions more gracefully. | |
1597 | * | |
1598 | * @internal vnode package internal use only | |
1599 | */ | |
1600 | static int | |
1601 | VInvalidateVnodesByVolume_r(Volume * vp, | |
1602 | IHandle_t *** vec_out, | |
1603 | size_t * vec_len_out) | |
1604 | { | |
1605 | int ret = 0; | |
1606 | Vnode *vnp, *nvnp; | |
1607 | size_t i = 0, vec_len; | |
1608 | IHandle_t **ih_vec, **ih_vec_new; | |
1609 | ||
1610 | #ifdef AFS_DEMAND_ATTACH_FS | |
1611 | VOL_UNLOCK; | |
1612 | #endif /* AFS_DEMAND_ATTACH_FS */ | |
1613 | ||
1614 | vec_len = IH_VEC_BASE_SIZE; | |
1615 | ih_vec = malloc(sizeof(IHandle_t *) * vec_len); | |
1616 | #ifdef AFS_DEMAND_ATTACH_FS | |
1617 | VOL_LOCK; | |
1618 | #endif | |
1619 | if (ih_vec == NULL) | |
1620 | return ENOMEM; | |
1621 | ||
1622 | /* | |
1623 | * Traverse the volume's vnode list. Pull all the ihandles out into a | |
1624 | * thread-private array for later asynchronous processing. | |
1625 | */ | |
1626 | #ifdef AFS_DEMAND_ATTACH_FS | |
1627 | restart_traversal: | |
1628 | #endif | |
1629 | for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) { | |
1630 | if (vnp->handle != NULL) { | |
1631 | if (i == vec_len) { | |
1632 | #ifdef AFS_DEMAND_ATTACH_FS | |
1633 | VOL_UNLOCK; | |
1634 | #endif | |
1635 | vec_len += IH_VEC_INCREMENT; | |
1636 | ih_vec_new = realloc(ih_vec, sizeof(IHandle_t *) * vec_len); | |
1637 | #ifdef AFS_DEMAND_ATTACH_FS | |
1638 | VOL_LOCK; | |
1639 | #endif | |
1640 | if (ih_vec_new == NULL) { | |
1641 | ret = ENOMEM; | |
1642 | goto done; | |
1643 | } | |
1644 | ih_vec = ih_vec_new; | |
1645 | #ifdef AFS_DEMAND_ATTACH_FS | |
1646 | /* | |
1647 | * Theoretically, the volume's VVn list should not change | |
1648 | * because the volume is in an exclusive state. For the | |
1649 | * sake of safety, we will restart the traversal from the | |
1650 | * the beginning (which is not expensive because we're | |
1651 | * deleting the items from the list as we go). | |
1652 | */ | |
1653 | goto restart_traversal; | |
1654 | #endif | |
1655 | } | |
1656 | ih_vec[i++] = vnp->handle; | |
1657 | vnp->handle = NULL; | |
1658 | } | |
1659 | DeleteFromVVnList(vnp); | |
1660 | VInvalidateVnode_r(vnp); | |
1661 | } | |
1662 | ||
1663 | done: | |
1664 | *vec_out = ih_vec; | |
1665 | *vec_len_out = i; | |
1666 | ||
1667 | return ret; | |
1668 | } | |
1669 | ||
1670 | /* VCloseVnodeFiles - called when a volume is going off line. All open | |
1671 | * files for vnodes in that volume are closed. This might be excessive, | |
1672 | * since we may only be taking one volume of a volume group offline. | |
1673 | */ | |
1674 | void | |
1675 | VCloseVnodeFiles_r(Volume * vp) | |
1676 | { | |
1677 | #ifdef AFS_DEMAND_ATTACH_FS | |
1678 | VolState vol_state_save; | |
1679 | #endif | |
1680 | IHandle_t ** ih_vec; | |
1681 | size_t i, vec_len; | |
1682 | ||
1683 | #ifdef AFS_DEMAND_ATTACH_FS | |
1684 | vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_CLOSE); | |
1685 | #endif /* AFS_DEMAND_ATTACH_FS */ | |
1686 | ||
1687 | /* XXX need better error handling here */ | |
1688 | opr_Verify(VInvalidateVnodesByVolume_r(vp, &ih_vec, | |
1689 | &vec_len) == 0); | |
1690 | ||
1691 | /* | |
1692 | * DAFS: | |
1693 | * now we drop VOL_LOCK while we perform some potentially very | |
1694 | * expensive operations in the background | |
1695 | */ | |
1696 | #ifdef AFS_DEMAND_ATTACH_FS | |
1697 | VOL_UNLOCK; | |
1698 | #endif | |
1699 | ||
1700 | for (i = 0; i < vec_len; i++) { | |
1701 | IH_REALLYCLOSE(ih_vec[i]); | |
1702 | IH_RELEASE(ih_vec[i]); | |
1703 | } | |
1704 | ||
1705 | free(ih_vec); | |
1706 | ||
1707 | #ifdef AFS_DEMAND_ATTACH_FS | |
1708 | VOL_LOCK; | |
1709 | VChangeState_r(vp, vol_state_save); | |
1710 | #endif /* AFS_DEMAND_ATTACH_FS */ | |
1711 | } | |
1712 | ||
1713 | ||
1714 | /** | |
1715 | * shut down all vnode cache state for a given volume. | |
1716 | * | |
1717 | * @param[in] vp volume object pointer | |
1718 | * | |
1719 | * @pre VOL_LOCK is held | |
1720 | * | |
1721 | * @post all file descriptors closed. | |
1722 | * all inode handles released. | |
1723 | * all vnode cache objects disassociated from volume. | |
1724 | * | |
1725 | * @note for DAFS, these operations are performed outside the vol glock under | |
1726 | * volume exclusive state VOL_STATE_VNODE_RELEASE. Please further note | |
1727 | * that it would be a bug to acquire and release a volume reservation | |
1728 | * during this exclusive operation. This is due to the fact that we are | |
1729 | * generally called during the refcount 1->0 transition. | |
1730 | * | |
1731 | * @todo we should handle failures in VInvalidateVnodesByVolume_r more | |
1732 | * gracefully. | |
1733 | * | |
1734 | * @see VInvalidateVnodesByVolume_r | |
1735 | * | |
1736 | * @internal this routine is internal to the volume package | |
1737 | */ | |
1738 | void | |
1739 | VReleaseVnodeFiles_r(Volume * vp) | |
1740 | { | |
1741 | #ifdef AFS_DEMAND_ATTACH_FS | |
1742 | VolState vol_state_save; | |
1743 | #endif | |
1744 | IHandle_t ** ih_vec; | |
1745 | size_t i, vec_len; | |
1746 | ||
1747 | #ifdef AFS_DEMAND_ATTACH_FS | |
1748 | vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_RELEASE); | |
1749 | #endif /* AFS_DEMAND_ATTACH_FS */ | |
1750 | ||
1751 | /* XXX need better error handling here */ | |
1752 | opr_Verify(VInvalidateVnodesByVolume_r(vp, &ih_vec, | |
1753 | &vec_len) == 0); | |
1754 | ||
1755 | /* | |
1756 | * DAFS: | |
1757 | * now we drop VOL_LOCK while we perform some potentially very | |
1758 | * expensive operations in the background | |
1759 | */ | |
1760 | #ifdef AFS_DEMAND_ATTACH_FS | |
1761 | VOL_UNLOCK; | |
1762 | #endif | |
1763 | ||
1764 | for (i = 0; i < vec_len; i++) { | |
1765 | IH_RELEASE(ih_vec[i]); | |
1766 | } | |
1767 | ||
1768 | free(ih_vec); | |
1769 | ||
1770 | #ifdef AFS_DEMAND_ATTACH_FS | |
1771 | VOL_LOCK; | |
1772 | VChangeState_r(vp, vol_state_save); | |
1773 | #endif /* AFS_DEMAND_ATTACH_FS */ | |
1774 | } |