libafs: Remove afs_read duplication
[openafs.git] / src / afs / VNOPS / afs_vnop_read.c
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
10 /*
11  * Implements:
12  * afs_MemRead
13  * afs_PrefetchChunk
14  * afs_UFSRead
15  * 
16  */
17
18 #include <afsconfig.h>
19 #include "afs/param.h"
20
21
22 #include "afs/sysincludes.h"    /* Standard vendor system headers */
23 #include "afsincludes.h"        /* Afs-based standard headers */
24 #include "afs/afs_stats.h"      /* statistics */
25 #include "afs/afs_cbqueue.h"
26 #include "afs/nfsclient.h"
27 #include "afs/afs_osidnlc.h"
28 #include "afs/afs_osi.h"
29
30
31 extern char afs_zeros[AFS_ZEROS];
32
33 /* Imported variables */
34 extern afs_rwlock_t afs_xdcache;
35 extern unsigned char *afs_indexFlags;
36 extern afs_hyper_t *afs_indexTimes;     /* Dcache entry Access times */
37 extern afs_hyper_t afs_indexCounter;    /* Fake time for marking index */
38
39
40 /* Forward declarations */
41 void afs_PrefetchChunk(struct vcache *avc, struct dcache *adc,
42                        afs_ucred_t *acred, struct vrequest *areq);
43
44 int
45 afs_read(struct vcache *avc, struct uio *auio, afs_ucred_t *acred,
46          daddr_t albn, struct buf **abpp, int noLock)
47 {
48     afs_size_t totalLength;
49     afs_size_t transferLength;
50     afs_size_t filePos;
51     afs_size_t offset, len, tlen;
52     afs_int32 trimlen;
53     struct dcache *tdc = 0;
54     afs_int32 error, trybusy = 1;
55 #ifdef AFS_DARWIN80_ENV
56     uio_t tuiop = NULL;
57 #else
58     struct uio tuio;
59     struct uio *tuiop = &tuio;
60     struct iovec *tvec = NULL;
61 #endif
62     afs_int32 code;
63     struct vrequest treq;
64
65     AFS_STATCNT(afs_read);
66
67     if (avc->vc_error)
68         return EIO;
69
70     AFS_DISCON_LOCK();
71
72     /* check that we have the latest status info in the vnode cache */
73     if ((code = afs_InitReq(&treq, acred)))
74         goto out;
75
76     if (!noLock) {
77         if (!avc)
78             osi_Panic("null avc in afs_GenericRead");
79
80         code = afs_VerifyVCache(avc, &treq);
81         if (code) {
82             code = afs_CheckCode(code, &treq, 8);       /* failed to get it */
83             goto out;
84         }
85     }
86 #ifndef AFS_VM_RDWR_ENV
87     if (AFS_NFSXLATORREQ(acred)) {
88         if (!afs_AccessOK
89             (avc, PRSFS_READ, &treq,
90              CHECK_MODE_BITS | CMB_ALLOW_EXEC_AS_READ)) {
91             code = afs_CheckCode(EACCES, &treq, 9);
92             goto out;
93         }
94     }
95 #endif
96
97 #ifndef AFS_DARWIN80_ENV
98     tvec = osi_AllocSmallSpace(sizeof(struct iovec));
99 #endif
100     totalLength = AFS_UIO_RESID(auio);
101     filePos = AFS_UIO_OFFSET(auio);
102     afs_Trace4(afs_iclSetp, CM_TRACE_READ, ICL_TYPE_POINTER, avc,
103                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(filePos), ICL_TYPE_INT32,
104                totalLength, ICL_TYPE_OFFSET,
105                ICL_HANDLE_OFFSET(avc->f.m.Length));
106     error = 0;
107     transferLength = 0;
108     if (!noLock)
109         ObtainReadLock(&avc->lock);
110 #if     defined(AFS_TEXT_ENV) && !defined(AFS_VM_RDWR_ENV)
111     if (avc->flushDV.high == AFS_MAXDV && avc->flushDV.low == AFS_MAXDV) {
112         hset(avc->flushDV, avc->f.m.DataVersion);
113     }
114 #endif
115
116     /*
117      * Locks held:
118      * avc->lock(R)
119      */
120     if (filePos >= avc->f.m.Length) {
121         if (len > AFS_ZEROS)
122             len = sizeof(afs_zeros);    /* and in 0 buffer */
123         len = 0;
124 #ifdef AFS_DARWIN80_ENV
125         trimlen = len;
126         tuiop = afsio_darwin_partialcopy(auio, trimlen);
127 #else
128         afsio_copy(auio, &tuio, tvec);
129         trimlen = len;
130         afsio_trim(&tuio, trimlen);
131 #endif
132         AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, tuiop, code);
133     }
134
135     while (avc->f.m.Length > 0 && totalLength > 0) {
136         /* read all of the cached info */
137         if (filePos >= avc->f.m.Length)
138             break;              /* all done */
139         if (noLock) {
140             if (tdc) {
141                 ReleaseReadLock(&tdc->lock);
142                 afs_PutDCache(tdc);
143             }
144             tdc = afs_FindDCache(avc, filePos);
145             if (tdc) {
146                 ObtainReadLock(&tdc->lock);
147                 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
148                 len = tdc->validPos - filePos;
149             }
150         } else {
151             /* a tricky question: does the presence of the DFFetching flag
152              * mean that we're fetching the latest version of the file?  No.
153              * The server could update the file as soon as the fetch responsible
154              * for the setting of the DFFetching flag completes.
155              * 
156              * However, the presence of the DFFetching flag (visible under
157              * a dcache read lock since it is set and cleared only under a
158              * dcache write lock) means that we're fetching as good a version
159              * as was known to this client at the time of the last call to
160              * afs_VerifyVCache, since the latter updates the stat cache's
161              * m.DataVersion field under a vcache write lock, and from the
162              * time that the DFFetching flag goes on in afs_GetDCache (before
163              * the fetch starts), to the time it goes off (after the fetch
164              * completes), afs_GetDCache keeps at least a read lock on the
165              * vcache entry.
166              * 
167              * This means that if the DFFetching flag is set, we can use that
168              * data for any reads that must come from the current version of
169              * the file (current == m.DataVersion).
170              * 
171              * Another way of looking at this same point is this: if we're
172              * fetching some data and then try do an afs_VerifyVCache, the
173              * VerifyVCache operation will not complete until after the
174              * DFFetching flag is turned off and the dcache entry's f.versionNo
175              * field is updated.
176              * 
177              * Note, by the way, that if DFFetching is set,
178              * m.DataVersion > f.versionNo (the latter is not updated until
179              * after the fetch completes).
180              */
181             if (tdc) {
182                 ReleaseReadLock(&tdc->lock);
183                 afs_PutDCache(tdc);     /* before reusing tdc */
184             }
185             tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 2);
186             if (!tdc) {
187                 error = ENETDOWN;
188                 break;
189             }
190
191             ObtainReadLock(&tdc->lock);
192             /* now, first try to start transfer, if we'll need the data.  If
193              * data already coming, we don't need to do this, obviously.  Type
194              * 2 requests never return a null dcache entry, btw.
195              */
196             if (!(tdc->dflags & DFFetching)
197                 && !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
198                 /* have cache entry, it is not coming in now,
199                  * and we'll need new data */
200               tagain:
201                 if (trybusy && !afs_BBusy()) {
202                     struct brequest *bp;
203                     /* daemon is not busy */
204                     ObtainSharedLock(&tdc->mflock, 665);
205                     if (!(tdc->mflags & DFFetchReq)) {
206                         /* start the daemon (may already be running, however) */
207                         UpgradeSToWLock(&tdc->mflock, 666);
208                         tdc->mflags |= DFFetchReq;
209                         bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred,
210                                         (afs_size_t) filePos, (afs_size_t) 0,
211                                         tdc, NULL, NULL);
212                         if (!bp) {
213                             /* Bkg table full; retry deadlocks */
214                             tdc->mflags &= ~DFFetchReq;
215                             trybusy = 0;        /* Avoid bkg daemon since they're too busy */
216                             ReleaseWriteLock(&tdc->mflock);
217                             goto tagain;
218                         }
219                         ConvertWToSLock(&tdc->mflock);
220                         /* don't use bp pointer! */
221                     }
222                     code = 0;
223                     ConvertSToRLock(&tdc->mflock);
224                     while (!code && tdc->mflags & DFFetchReq) {
225                         afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT,
226                                    ICL_TYPE_STRING, __FILE__, ICL_TYPE_INT32,
227                                    __LINE__, ICL_TYPE_POINTER, tdc,
228                                    ICL_TYPE_INT32, tdc->dflags);
229                         /* don't need waiting flag on this one */
230                         ReleaseReadLock(&tdc->mflock);
231                         ReleaseReadLock(&tdc->lock);
232                         ReleaseReadLock(&avc->lock);
233                         code = afs_osi_SleepSig(&tdc->validPos);
234                         ObtainReadLock(&avc->lock);
235                         ObtainReadLock(&tdc->lock);
236                         ObtainReadLock(&tdc->mflock);
237                     }
238                     ReleaseReadLock(&tdc->mflock);
239                     if (code) {
240                         error = code;
241                         break;
242                     }
243                 }
244             }
245             /* now data may have started flowing in (if DFFetching is on).  If
246              * data is now streaming in, then wait for some interesting stuff.
247              */
248             code = 0;
249             while (!code && (tdc->dflags & DFFetching)
250                    && tdc->validPos <= filePos) {
251                 /* too early: wait for DFFetching flag to vanish,
252                  * or data to appear */
253                 afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING,
254                            __FILE__, ICL_TYPE_INT32, __LINE__,
255                            ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32,
256                            tdc->dflags);
257                 ReleaseReadLock(&tdc->lock);
258                 ReleaseReadLock(&avc->lock);
259                 code = afs_osi_SleepSig(&tdc->validPos);
260                 ObtainReadLock(&avc->lock);
261                 ObtainReadLock(&tdc->lock);
262             }
263             if (code) {
264                 error = code;
265                 break;
266             }
267             /* fetching flag gone, data is here, or we never tried 
268              * (BBusy for instance) */
269             if (tdc->dflags & DFFetching) {
270                 /* still fetching, some new data is here: 
271                  * compute length and offset */
272                 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
273                 len = tdc->validPos - filePos;
274             } else {
275                 /* no longer fetching, verify data version 
276                  * (avoid new GetDCache call) */
277                 if (hsame(avc->f.m.DataVersion, tdc->f.versionNo)
278                     && ((len = tdc->validPos - filePos) > 0)) {
279                     offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
280                 } else {
281                     /* don't have current data, so get it below */
282                     afs_Trace3(afs_iclSetp, CM_TRACE_VERSIONNO,
283                                ICL_TYPE_INT64, ICL_HANDLE_OFFSET(filePos),
284                                ICL_TYPE_HYPER, &avc->f.m.DataVersion,
285                                ICL_TYPE_HYPER, &tdc->f.versionNo);
286                     ReleaseReadLock(&tdc->lock);
287                     afs_PutDCache(tdc);
288                     tdc = NULL;
289                 }
290             }
291
292             if (!tdc) {
293                 /* If we get, it was not possible to start the
294                  * background daemon. With flag == 1 afs_GetDCache
295                  * does the FetchData rpc synchronously.
296                  */
297                 ReleaseReadLock(&avc->lock);
298                 tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 1);
299                 ObtainReadLock(&avc->lock);
300                 if (tdc)
301                     ObtainReadLock(&tdc->lock);
302             }
303         }
304
305         afs_Trace3(afs_iclSetp, CM_TRACE_VNODEREAD, ICL_TYPE_POINTER, tdc,
306                    ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(offset),
307                    ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(len));
308         if (!tdc) {
309             error = EIO;
310             break;
311         }
312
313         /*
314          * Locks held:
315          * avc->lock(R)
316          * tdc->lock(R)
317          */
318
319         if (len > totalLength)
320             len = totalLength;  /* will read len bytes */
321         if (len <= 0) {         /* shouldn't get here if DFFetching is on */
322             /* read past the end of a chunk, may not be at next chunk yet, and yet
323              * also not at eof, so may have to supply fake zeros */
324             len = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset;       /* bytes left in chunk addr space */
325             if (len > totalLength)
326                 len = totalLength;      /* and still within xfr request */
327             tlen = avc->f.m.Length - offset;    /* and still within file */
328             if (len > tlen)
329                 len = tlen;
330             if (len > AFS_ZEROS)
331                 len = sizeof(afs_zeros);        /* and in 0 buffer */
332 #ifdef AFS_DARWIN80_ENV
333             trimlen = len;
334             tuiop = afsio_darwin_partialcopy(auio, trimlen);
335 #else
336             afsio_copy(auio, &tuio, tvec);
337             trimlen = len;
338             afsio_trim(&tuio, trimlen);
339 #endif
340             AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, tuiop, code);
341             if (code) {
342                 error = code;
343                 break;
344             }
345         } else {
346             /* get the data from the cache */
347
348             /* mung uio structure to be right for this transfer */
349 #ifdef AFS_DARWIN80_ENV
350             trimlen = len;
351             tuiop = afsio_darwin_partialcopy(auio, trimlen);
352             uio_setoffset(tuiop, offset);
353 #else
354             afsio_copy(auio, &tuio, tvec);
355             trimlen = len;
356             afsio_trim(&tuio, trimlen);
357             tuio.afsio_offset = offset;
358 #endif
359
360             code = (*(afs_cacheType->vreadUIO))(&tdc->f.inode, tuiop);
361
362             if (code) {
363                 error = code;
364                 break;
365             }
366         }
367         /* otherwise we've read some, fixup length, etc and continue with next seg */
368         len = len - AFS_UIO_RESID(tuiop);       /* compute amount really transferred */
369         trimlen = len;
370         afsio_skip(auio, trimlen);      /* update input uio structure */
371         totalLength -= len;
372         transferLength += len;
373         filePos += len;
374
375         if (len <= 0)
376             break;              /* surprise eof */
377 #ifdef AFS_DARWIN80_ENV
378         if (tuiop) {
379             uio_free(tuiop);
380             tuiop = 0;
381         }
382 #endif
383     }                           /* the whole while loop */
384
385     /*
386      * Locks held:
387      * avc->lock(R)
388      * tdc->lock(R) if tdc
389      */
390
391     /* if we make it here with tdc non-zero, then it is the last chunk we
392      * dealt with, and we have to release it when we're done.  We hold on
393      * to it in case we need to do a prefetch.
394      */
395     if (tdc) {
396         ReleaseReadLock(&tdc->lock);
397 #if !defined(AFS_VM_RDWR_ENV)
398         /* try to queue prefetch, if needed */
399         if (!noLock) {
400             if (!(tdc->mflags &DFNextStarted))
401                 afs_PrefetchChunk(avc, tdc, acred, &treq);
402         }
403 #endif
404         afs_PutDCache(tdc);
405     }
406     if (!noLock)
407         ReleaseReadLock(&avc->lock);
408
409     code = afs_CheckCode(error, &treq, 10);
410
411 #ifdef AFS_DARWIN80_ENV
412     if (tuiop)
413        uio_free(tuiop);
414 #else
415     osi_FreeSmallSpace(tvec);
416 #endif
417
418 out:
419     AFS_DISCON_UNLOCK();
420     return code;
421 }
422
423 /* called with the dcache entry triggering the fetch, the vcache entry involved,
424  * and a vrequest for the read call.  Marks the dcache entry as having already
425  * triggered a prefetch, starts the prefetch going and sets the DFFetchReq
426  * flag in the prefetched block, so that the next call to read knows to wait
427  * for the daemon to start doing things.
428  *
429  * This function must be called with the vnode at least read-locked, and
430  * no locks on the dcache, because it plays around with dcache entries.
431  */
432 void
433 afs_PrefetchChunk(struct vcache *avc, struct dcache *adc,
434                   afs_ucred_t *acred, struct vrequest *areq)
435 {
436     struct dcache *tdc;
437     afs_size_t offset;
438     afs_size_t j1, j2;          /* junk vbls for GetDCache to trash */
439
440     offset = adc->f.chunk + 1;  /* next chunk we'll need */
441     offset = AFS_CHUNKTOBASE(offset);   /* base of next chunk */
442     ObtainReadLock(&adc->lock);
443     ObtainSharedLock(&adc->mflock, 662);
444     if (offset < avc->f.m.Length && !(adc->mflags & DFNextStarted)
445         && !afs_BBusy()) {
446         struct brequest *bp;
447
448         UpgradeSToWLock(&adc->mflock, 663);
449         adc->mflags |= DFNextStarted;   /* we've tried to prefetch for this guy */
450         ReleaseWriteLock(&adc->mflock);
451         ReleaseReadLock(&adc->lock);
452
453         tdc = afs_GetDCache(avc, offset, areq, &j1, &j2, 2);    /* type 2 never returns 0 */
454         /*
455          * In disconnected mode, type 2 can return 0 because it doesn't
456          * make any sense to allocate a dcache we can never fill
457          */
458          if (tdc == NULL)
459              return;
460
461         ObtainSharedLock(&tdc->mflock, 651);
462         if (!(tdc->mflags & DFFetchReq)) {
463             /* ask the daemon to do the work */
464             UpgradeSToWLock(&tdc->mflock, 652);
465             tdc->mflags |= DFFetchReq;  /* guaranteed to be cleared by BKG or GetDCache */
466             /* last parm (1) tells bkg daemon to do an afs_PutDCache when it is done,
467              * since we don't want to wait for it to finish before doing so ourselves.
468              */
469             bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred,
470                             (afs_size_t) offset, (afs_size_t) 1, tdc,
471                             (void *)0, (void *)0);
472             if (!bp) {
473                 /* Bkg table full; just abort non-important prefetching to avoid deadlocks */
474                 tdc->mflags &= ~DFFetchReq;
475                 ReleaseWriteLock(&tdc->mflock);
476                 afs_PutDCache(tdc);
477
478                 /*
479                  * DCLOCKXXX: This is a little sketchy, since someone else
480                  * could have already started a prefetch..  In practice,
481                  * this probably doesn't matter; at most it would cause an
482                  * extra slot in the BKG table to be used up when someone
483                  * prefetches this for the second time.
484                  */
485                 ObtainReadLock(&adc->lock);
486                 ObtainWriteLock(&adc->mflock, 664);
487                 adc->mflags &= ~DFNextStarted;
488                 ReleaseWriteLock(&adc->mflock);
489                 ReleaseReadLock(&adc->lock);
490             } else {
491                 ReleaseWriteLock(&tdc->mflock);
492             }
493         } else {
494             ReleaseSharedLock(&tdc->mflock);
495             afs_PutDCache(tdc);
496         }
497     } else {
498         ReleaseSharedLock(&adc->mflock);
499         ReleaseReadLock(&adc->lock);
500     }
501 }
502
503 int
504 afs_UFSReadUIO(afs_dcache_id_t *cacheId, struct uio *tuiop)
505 {
506     int code;
507     struct osi_file *tfile;
508
509     tfile = (struct osi_file *) osi_UFSOpen(cacheId);
510
511 #if defined(AFS_AIX41_ENV)
512     AFS_GUNLOCK();
513     code =
514         VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, tuiop, NULL, NULL,
515                   NULL, afs_osi_credp);
516     AFS_GLOCK();
517 #elif defined(AFS_AIX32_ENV)
518     code =
519         VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, tuiop, NULL, NULL);
520     /* Flush all JFS pages now for big performance gain in big file cases
521      * If we do something like this, must check to be sure that AFS file
522      * isn't mmapped... see afs_gn_map() for why.
523      */
524     /*
525    if (tfile->vnode->v_gnode && tfile->vnode->v_gnode->gn_seg) {
526    any different ways to do similar things:
527    so far, the best performing one is #2, but #1 might match it if we
528    straighten out the confusion regarding which pages to flush.  It 
529    really does matter.
530    1.       vm_flushp(tfile->vnode->v_gnode->gn_seg, 0, len/PAGESIZE - 1);
531    2.       vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE, 
532                         (len + PAGESIZE-1)/PAGESIZE);
533    3.       vms_inactive(tfile->vnode->v_gnode->gn_seg) Doesn't work correctly
534    4.       vms_delete(tfile->vnode->v_gnode->gn_seg) probably also fails
535             tfile->vnode->v_gnode->gn_seg = NULL;
536    5.       deletep
537    6.       ipgrlse
538    7.       ifreeseg
539           Unfortunately, this seems to cause frequent "cache corruption" episodes.
540             vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE, 
541                         (len + PAGESIZE-1)/PAGESIZE);
542           }     
543 */
544 #elif defined(AFS_AIX_ENV)
545     code =
546         VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, (off_t) & offset,
547                   tuiop, NULL, NULL, -1);
548 #elif defined(AFS_SUN5_ENV)
549     AFS_GUNLOCK();
550 #ifdef AFS_SUN510_ENV
551     {
552         caller_context_t ct;
553         VOP_RWLOCK(tfile->vnode, 0, &ct);
554         code = VOP_READ(tfile->vnode, tuiop, 0, afs_osi_credp, &ct);
555         VOP_RWUNLOCK(tfile->vnode, 0, &ct);
556     }
557 #else
558     VOP_RWLOCK(tfile->vnode, 0);
559     code = VOP_READ(tfile->vnode, tuiop, 0, afs_osi_credp);
560     VOP_RWUNLOCK(tfile->vnode, 0);
561 #endif
562     AFS_GLOCK();
563 #elif defined(AFS_SGI_ENV)
564     AFS_GUNLOCK();
565     AFS_VOP_RWLOCK(tfile->vnode, VRWLOCK_READ);
566     AFS_VOP_READ(tfile->vnode, tuiop, IO_ISLOCKED, afs_osi_credp,
567                  code);
568     AFS_VOP_RWUNLOCK(tfile->vnode, VRWLOCK_READ);
569     AFS_GLOCK();
570 #elif defined(AFS_HPUX100_ENV)
571     AFS_GUNLOCK();
572     code = VOP_RDWR(tfile->vnode, tuiop, UIO_READ, 0, afs_osi_credp);
573     AFS_GLOCK();
574 #elif defined(AFS_LINUX20_ENV)
575     AFS_GUNLOCK();
576     code = osi_rdwr(tfile, tuiop, UIO_READ);
577     AFS_GLOCK();
578 #elif defined(AFS_DARWIN80_ENV)
579     AFS_GUNLOCK();
580     code = VNOP_READ(tfile->vnode, tuiop, 0, afs_osi_ctxtp);
581     AFS_GLOCK();
582 #elif defined(AFS_DARWIN_ENV)
583     AFS_GUNLOCK();
584     VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, current_proc());
585     code = VOP_READ(tfile->vnode, tuiop, 0, afs_osi_credp);
586     VOP_UNLOCK(tfile->vnode, 0, current_proc());
587     AFS_GLOCK();
588 #elif defined(AFS_FBSD80_ENV)
589     AFS_GUNLOCK();
590     VOP_LOCK(tfile->vnode, LK_EXCLUSIVE);
591     code = VOP_READ(tfile->vnode, tuiop, 0, afs_osi_credp);
592     VOP_UNLOCK(tfile->vnode, 0);
593     AFS_GLOCK();
594 #elif defined(AFS_FBSD_ENV)
595     AFS_GUNLOCK();
596     VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, curthread);
597     code = VOP_READ(tfile->vnode, tuiop, 0, afs_osi_credp);
598     VOP_UNLOCK(tfile->vnode, 0, curthread);
599     AFS_GLOCK();
600 #elif defined(AFS_NBSD_ENV)
601     AFS_GUNLOCK();
602     VOP_LOCK(tfile->vnode, LK_EXCLUSIVE);
603     code = VOP_READ(tfile->vnode, tuiop, 0, afs_osi_credp);
604     VOP_UNLOCK(tfile->vnode, 0);
605     AFS_GLOCK();
606 #elif defined(AFS_XBSD_ENV)
607     AFS_GUNLOCK();
608     VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, curproc);
609     code = VOP_READ(tfile->vnode, tuiop, 0, afs_osi_credp);
610     VOP_UNLOCK(tfile->vnode, 0, curproc);
611     AFS_GLOCK();
612 #else
613     code = VOP_RDWR(tfile->vnode, tuiop, UIO_READ, 0, afs_osi_credp);
614 #endif
615     osi_UFSClose(tfile);
616
617     return code;
618 }