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