2 * Copyright 2000, International Business Machines Corporation and others.
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
19 #include "../afs/param.h" /* Should be always first */
20 #include "../afs/sysincludes.h" /* Standard vendor system headers */
21 #include "../afs/afsincludes.h" /* Afs-based standard headers */
22 #include "../afs/afs_stats.h" /* statistics */
23 #include "../afs/afs_cbqueue.h"
24 #include "../afs/nfsclient.h"
25 #include "../afs/afs_osidnlc.h"
28 extern char afs_zeros[AFS_ZEROS];
31 afs_int32 nihints; /* # of above actually in-use */
35 /* Imported variables */
36 extern afs_rwlock_t afs_xdcache;
37 extern unsigned char *afs_indexFlags;
38 extern afs_hyper_t *afs_indexTimes; /* Dcache entry Access times */
39 extern afs_hyper_t afs_indexCounter; /* Fake time for marking index */
42 /* Forward declarations */
43 void afs_PrefetchChunk(struct vcache *avc, struct dcache *adc,
44 struct AFS_UCRED *acred, struct vrequest *areq);
46 afs_MemRead(avc, auio, acred, albn, abpp, noLock)
47 register struct vcache *avc;
49 struct AFS_UCRED *acred;
53 afs_int32 totalLength;
54 afs_int32 transferLength;
57 afs_int32 offset, len, error, trybusy=1;
64 AFS_STATCNT(afs_MemRead);
68 /* check that we have the latest status info in the vnode cache */
69 if (code = afs_InitReq(&treq, acred)) return code;
71 code = afs_VerifyVCache(avc, &treq);
73 code = afs_CheckCode(code, &treq, 8); /* failed to get it */
78 #ifndef AFS_VM_RDWR_ENV
79 if (AFS_NFSXLATORREQ(acred)) {
80 if (!afs_AccessOK(avc, PRSFS_READ, &treq,
81 CHECK_MODE_BITS|CMB_ALLOW_EXEC_AS_READ)) {
82 return afs_CheckCode(EACCES, &treq, 9);
87 tvec = (struct iovec *) osi_AllocSmallSpace(sizeof(struct iovec));
88 totalLength = auio->afsio_resid;
89 filePos = auio->afsio_offset;
90 afs_Trace4(afs_iclSetp, CM_TRACE_READ, ICL_TYPE_POINTER, avc,
91 ICL_TYPE_INT32, filePos, ICL_TYPE_INT32, totalLength,
92 ICL_TYPE_INT32, avc->m.Length);
96 ObtainReadLock(&avc->lock);
97 #if defined(AFS_TEXT_ENV) && !defined(AFS_VM_RDWR_ENV)
98 if (avc->flushDV.high == AFS_MAXDV && avc->flushDV.low == AFS_MAXDV) {
99 hset(avc->flushDV, avc->m.DataVersion);
102 while (totalLength > 0) {
103 /* read all of the cached info */
104 if (filePos >= avc->m.Length) break; /* all done */
106 if (tdc) afs_PutDCache(tdc);
107 tdc = afs_FindDCache(avc, filePos);
109 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
110 len = tdc->f.chunkBytes - offset;
113 /* a tricky question: does the presence of the DFFetching flag
114 mean that we're fetching the latest version of the file? No.
115 The server could update the file as soon as the fetch responsible
116 for the setting of the DFFetching flag completes.
118 However, the presence of the DFFetching flag (visible under a
119 read lock since it is set and cleared only under a write lock)
120 means that we're fetching as good a version as was known to this
121 client at the time of the last call to afs_VerifyVCache, since
122 the latter updates the stat cache's m.DataVersion field under a
123 write lock, and from the time that the DFFetching flag goes on
124 (before the fetch starts), to the time it goes off (after the
125 fetch completes), afs_GetDCache keeps at least a read lock
126 (actually it keeps an S lock) on the cache entry.
128 This means that if the DFFetching flag is set, we can use that
129 data for any reads that must come from the current version of
130 the file (current == m.DataVersion).
132 Another way of looking at this same point is this: if we're
133 fetching some data and then try do an afs_VerifyVCache, the
134 VerifyVCache operation will not complete until after the
135 DFFetching flag is turned off and the dcache entry's f.versionNo
138 Note, by the way, that if DFFetching is set,
139 m.DataVersion > f.versionNo (the latter is not updated until
140 after the fetch completes).
142 if (tdc) afs_PutDCache(tdc); /* before reusing tdc */
143 tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 2);
144 /* now, first try to start transfer, if we'll need the data. If
145 * data already coming, we don't need to do this, obviously. Type
146 * 2 requests never return a null dcache entry, btw.
148 if (!(tdc->flags & DFFetching)
149 && !hsame(avc->m.DataVersion, tdc->f.versionNo)) {
150 /* have cache entry, it is not coming in now,
151 * and we'll need new data */
153 if (trybusy && !afs_BBusy()) {
155 /* daemon is not busy */
156 if (!(tdc->flags & DFFetchReq)) {
157 /* start the daemon (may already be running, however) */
158 tdc->flags |= DFFetchReq;
159 bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred,
160 (long)filePos, (long) tdc, 0L, 0L);
162 tdc->flags &= ~DFFetchReq;
163 trybusy = 0; /* Avoid bkg daemon since they're too busy */
166 /* don't use bp pointer! */
168 while (tdc->flags & DFFetchReq) {
169 /* don't need waiting flag on this one */
170 ReleaseReadLock(&avc->lock);
171 afs_osi_Sleep(&tdc->validPos);
172 ObtainReadLock(&avc->lock);
176 /* now data may have started flowing in (if DFFetching is on). If
177 * data is now streaming in, then wait for some interesting stuff. */
178 while ((tdc->flags & DFFetching) && tdc->validPos <= filePos) {
179 /* too early: wait for DFFetching flag to vanish, or data to appear */
180 tdc->flags |= DFWaiting;
181 ReleaseReadLock(&avc->lock);
182 afs_osi_Sleep(&tdc->validPos);
183 ObtainReadLock(&avc->lock);
185 /* fetching flag gone, data is here, or we never tried (BBusy for instance) */
186 if (tdc->flags & DFFetching) {
187 /* still fetching, some new data is here: compute length and offset */
188 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
189 len = tdc->validPos - filePos;
192 /* no longer fetching, verify data version (avoid new GetDCache call) */
193 if (hsame(avc->m.DataVersion, tdc->f.versionNo)) {
194 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
195 len = tdc->f.chunkBytes - offset;
198 /* don't have current data, so get it below */
200 tdc = (struct dcache *) 0;
205 ReleaseReadLock(&avc->lock);
206 tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 1);
207 ObtainReadLock(&avc->lock);
215 if (len > totalLength) len = totalLength; /* will read len bytes */
216 if (len <= 0) { /* shouldn't get here if DFFetching is on */
217 /* read past the end of a chunk, may not be at next chunk yet, and yet
218 also not at eof, so may have to supply fake zeros */
219 len = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; /* bytes left in chunk addr space */
220 if (len > totalLength) len = totalLength; /* and still within xfr request */
221 code = avc->m.Length - offset; /* and still within file */
222 if (len > code) len = code;
223 if (len > AFS_ZEROS) len = sizeof(afs_zeros); /* and in 0 buffer */
224 afsio_copy(auio, &tuio, tvec);
225 afsio_trim(&tuio, len);
226 AFS_UIOMOVE(afs_zeros, len, UIO_READ, &tuio, code);
233 /* get the data from the mem cache */
235 /* mung uio structure to be right for this transfer */
236 afsio_copy(auio, &tuio, tvec);
237 afsio_trim(&tuio, len);
238 tuio.afsio_offset = offset;
240 code = afs_MemReadUIO(tdc->f.inode, &tuio);
247 /* otherwise we've read some, fixup length, etc and continue with next seg */
248 len = len - tuio.afsio_resid; /* compute amount really transferred */
249 afsio_skip(auio, len); /* update input uio structure */
251 transferLength += len;
254 if (len <= 0) break; /* surprise eof */
255 } /* the whole while loop */
257 /* if we make it here with tdc non-zero, then it is the last chunk we
258 * dealt with, and we have to release it when we're done. We hold on
259 * to it in case we need to do a prefetch.
262 #ifndef AFS_VM_RDWR_ENV
263 /* try to queue prefetch, if needed */
264 if (!(tdc->flags & DFNextStarted) && !noLock) {
265 afs_PrefetchChunk(avc, tdc, acred, &treq);
271 ReleaseReadLock(&avc->lock);
272 osi_FreeSmallSpace(tvec);
273 error = afs_CheckCode(error, &treq, 10);
277 /* called with the dcache entry triggering the fetch, the vcache entry involved,
278 * and a vrequest for the read call. Marks the dcache entry as having already
279 * triggered a prefetch, starts the prefetch going and sets the DFFetchReq
280 * flag in the prefetched block, so that the next call to read knows to wait
281 * for the daemon to start doing things.
283 * This function must be called with the vnode at least read-locked
284 * because it plays around with dcache entries.
286 void afs_PrefetchChunk(struct vcache *avc, struct dcache *adc,
287 struct AFS_UCRED *acred, struct vrequest *areq)
289 register struct dcache *tdc;
290 register afs_int32 offset;
291 afs_int32 j1, j2; /* junk vbls for GetDCache to trash */
293 offset = adc->f.chunk+1; /* next chunk we'll need */
294 offset = AFS_CHUNKTOBASE(offset); /* base of next chunk */
295 if (offset < avc->m.Length && !afs_BBusy()) {
297 adc->flags |= DFNextStarted; /* we've tried to prefetch for this guy */
298 tdc = afs_GetDCache(avc, offset, areq, &j1, &j2, 2); /* type 2 never returns 0 */
299 if (!(tdc->flags & DFFetchReq)) {
300 /* ask the daemon to do the work */
301 tdc->flags |= DFFetchReq; /* guaranteed to be cleared by BKG or GetDCache */
302 /* last parm (1) tells bkg daemon to do an afs_PutDCache when it is done,
303 * since we don't want to wait for it to finish before doing so ourselves.
306 mutex_exit(&tdc->lock);
308 bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred, (long)offset,
309 (long) tdc, 1L, 0L, 0L);
311 /* Bkg table full; just abort non-important prefetching to avoid deadlocks */
312 tdc->flags &= ~(DFNextStarted | DFFetchReq);
323 /* if the vcache is up-to-date, and the request fits entirely into the chunk
324 * that the hint here references, then we just use it quickly, otherwise we
325 * have to call the slow read.
327 * This could be generalized in several ways to take advantage of partial
328 * state even when all the chips don't fall the right way. For instance,
329 * if the hint is good and the first part of the read request can be
330 * satisfied from the chunk, then just do the read. After the read has
331 * completed, check to see if there's more. (Chances are there won't be.)
332 * If there is more, then just call afs_UFSReadSlow and let it do the rest.
334 * For the time being, I'm ignoring quick.f, but it should be used at
336 * do this in the future avc->quick.f = tfile; but I think it
337 * has to be done under a write lock, but don't want to wait on the
340 /* everywhere that a dcache can be freed (look for NULLIDX)
341 * probably does it under a write lock on xdcache. Need to invalidate
343 * Also need to worry about DFFetching, and IFFree, I think. */
344 static struct dcache *savedc = 0;
346 afs_UFSReadFast(avc, auio, acred, albn, abpp, noLock)
347 register struct vcache *avc;
349 struct AFS_UCRED *acred;
353 struct vrequest treq;
357 struct osi_file *tfile;
361 ObtainReadLock(&avc->lock);
362 ObtainReadLock(&afs_xdcache);
364 if ((avc->states & CStatd) /* up to date */
365 && (tdc = avc->quick.dc) && (tdc->index != NULLIDX)
366 && !(afs_indexFlags[tdc->index] & IFFree)) {
369 ReleaseReadLock(&afs_xdcache);
371 if ((tdc->stamp == avc->quick.stamp) /* hint matches */
372 && ((offDiff = (auio->afsio_offset - avc->quick.minLoc)) >= 0)
373 && (tdc->f.chunkBytes >= auio->afsio_resid + offDiff)
374 && !(tdc->flags & DFFetching)) { /* fits in chunk */
376 auio->afsio_offset -= avc->quick.minLoc;
378 tfile = (struct osi_file *)osi_UFSOpen(tdc->f.inode);
383 code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, auio, NULL, NULL, NULL, &afs_osi_cred);
387 code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, auio, NULL, NULL);
389 code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, (off_t)&auio->afsio_offset, auio, NULL, NULL, -1);
395 VOP_RWLOCK(tfile->vnode, 0);
396 code = VOP_READ(tfile->vnode, auio, 0, &afs_osi_cred);
397 VOP_RWUNLOCK(tfile->vnode, 0);
400 #if defined(AFS_SGI_ENV)
402 AFS_VOP_RWLOCK(tfile->vnode, VRWLOCK_READ);
403 AFS_VOP_READ(tfile->vnode, auio, IO_ISLOCKED, &afs_osi_cred, code);
404 AFS_VOP_RWUNLOCK(tfile->vnode, VRWLOCK_READ);
408 auio->uio_rw = UIO_READ;
410 VOP_READ(tfile->vnode, auio, 0, &afs_osi_cred, code);
412 #else /* AFS_OSF_ENV */
413 #if defined(AFS_HPUX100_ENV)
415 code = VOP_RDWR(tfile->vnode, auio, UIO_READ, 0, &afs_osi_cred);
418 #if defined(AFS_LINUX20_ENV)
420 code = osi_file_uio_rdwr(tfile, auio, UIO_READ);
423 #if defined(AFS_DARWIN_ENV)
425 VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, current_proc());
426 code = VOP_READ(tfile->vnode, auio, 0, &afs_osi_cred);
427 VOP_UNLOCK(tfile->vnode, 0, current_proc());
430 code = VOP_RDWR(tfile->vnode, auio, UIO_READ, 0, &afs_osi_cred);
438 auio->afsio_offset += avc->quick.minLoc;
440 /* Fix up LRU info */
441 hset(afs_indexTimes[tdc->index], afs_indexCounter);
442 hadd32(afs_indexCounter, 1);
445 #ifndef AFS_VM_RDWR_ENV
446 if (!(code = afs_InitReq(&treq, acred))&& (!(tdc->flags & DFNextStarted)))
447 afs_PrefetchChunk(avc, tdc, acred, &treq);
449 ReleaseReadLock(&avc->lock);
454 if (!tdc->f.chunkBytes) { /* debugging f.chunkBytes == 0 problem */
459 ReleaseReadLock(&afs_xdcache);
462 /* come here if fast path doesn't work for some reason or other */
464 ReleaseReadLock(&avc->lock);
465 return afs_UFSRead(avc, auio, acred, albn, abpp, noLock);
468 afs_UFSRead(avc, auio, acred, albn, abpp, noLock)
471 struct AFS_UCRED *acred;
475 afs_int32 totalLength;
476 afs_int32 transferLength;
478 struct dcache *tdc=0;
479 afs_int32 offset, len, error;
482 struct osi_file *tfile;
484 int munlocked, trybusy=1;
486 struct vrequest treq;
488 AFS_STATCNT(afs_UFSRead);
492 /* check that we have the latest status info in the vnode cache */
493 if (code = afs_InitReq(&treq, acred)) return code;
496 osi_Panic ("null avc in afs_UFSRead");
498 code = afs_VerifyVCache(avc, &treq);
500 code = afs_CheckCode(code, &treq, 11); /* failed to get it */
506 #ifndef AFS_VM_RDWR_ENV
507 if (AFS_NFSXLATORREQ(acred)) {
508 if (!afs_AccessOK(avc, PRSFS_READ, &treq,
509 CHECK_MODE_BITS|CMB_ALLOW_EXEC_AS_READ)) {
510 return afs_CheckCode(EACCES, &treq, 12);
515 tvec = (struct iovec *) osi_AllocSmallSpace(sizeof(struct iovec));
516 totalLength = auio->afsio_resid;
517 filePos = auio->afsio_offset;
518 afs_Trace4(afs_iclSetp, CM_TRACE_READ, ICL_TYPE_POINTER, avc,
519 ICL_TYPE_INT32, filePos, ICL_TYPE_INT32, totalLength,
520 ICL_TYPE_INT32, avc->m.Length);
524 ObtainReadLock(&avc->lock);
525 #if defined(AFS_TEXT_ENV) && !defined(AFS_VM_RDWR_ENV)
526 if (avc->flushDV.high == AFS_MAXDV && avc->flushDV.low == AFS_MAXDV) {
527 hset(avc->flushDV, avc->m.DataVersion);
531 while (totalLength > 0) {
532 /* read all of the cached info */
533 if (filePos >= avc->m.Length) break; /* all done */
535 if (tdc) afs_PutDCache(tdc);
536 tdc = afs_FindDCache(avc, filePos);
538 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
539 len = tdc->f.chunkBytes - offset;
542 /* a tricky question: does the presence of the DFFetching flag
543 mean that we're fetching the latest version of the file? No.
544 The server could update the file as soon as the fetch responsible
545 for the setting of the DFFetching flag completes.
547 However, the presence of the DFFetching flag (visible under a
548 read lock since it is set and cleared only under a write lock)
549 means that we're fetching as good a version as was known to this
550 client at the time of the last call to afs_VerifyVCache, since
551 the latter updates the stat cache's m.DataVersion field under a
552 write lock, and from the time that the DFFetching flag goes on
553 (before the fetch starts), to the time it goes off (after the
554 fetch completes), afs_GetDCache keeps at least a read lock
555 (actually it keeps an S lock) on the cache entry.
557 This means that if the DFFetching flag is set, we can use that
558 data for any reads that must come from the current version of
559 the file (current == m.DataVersion).
561 Another way of looking at this same point is this: if we're
562 fetching some data and then try do an afs_VerifyVCache, the
563 VerifyVCache operation will not complete until after the
564 DFFetching flag is turned off and the dcache entry's f.versionNo
567 Note, by the way, that if DFFetching is set,
568 m.DataVersion > f.versionNo (the latter is not updated until
569 after the fetch completes).
571 if (tdc) afs_PutDCache(tdc); /* before reusing tdc */
573 tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 2);
577 /* now, first try to start transfer, if we'll need the data. If
578 * data already coming, we don't need to do this, obviously. Type
579 * 2 requests never return a null dcache entry, btw. */
580 if (!(tdc->flags & DFFetching)
581 && !hsame(avc->m.DataVersion, tdc->f.versionNo)) {
582 /* have cache entry, it is not coming in now, and we'll need new data */
584 if (trybusy && !afs_BBusy()) {
586 /* daemon is not busy */
587 if (!(tdc->flags & DFFetchReq)) {
588 tdc->flags |= DFFetchReq;
590 mutex_exit(&tdc->lock);
593 bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred,
594 (long)filePos, (long) tdc, 0L, 0L);
596 /* Bkg table full; retry deadlocks */
597 tdc->flags &= ~DFFetchReq;
598 trybusy = 0; /* Avoid bkg daemon since they're too busy */
602 while (tdc->flags & DFFetchReq) {
603 /* don't need waiting flag on this one */
604 ReleaseReadLock(&avc->lock);
605 afs_osi_Sleep(&tdc->validPos);
606 ObtainReadLock(&avc->lock);
610 /* now data may have started flowing in (if DFFetching is on). If
611 * data is now streaming in, then wait for some interesting stuff. */
612 while ((tdc->flags & DFFetching) && tdc->validPos <= filePos) {
613 /* too early: wait for DFFetching flag to vanish, or data to appear */
614 tdc->flags |= DFWaiting;
615 ReleaseReadLock(&avc->lock);
616 afs_osi_Sleep(&tdc->validPos);
617 ObtainReadLock(&avc->lock);
619 /* fetching flag gone, data is here, or we never tried (BBusy for instance) */
620 if (tdc->flags & DFFetching) {
621 /* still fetching, some new data is here: compute length and offset */
622 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
623 len = tdc->validPos - filePos;
626 /* no longer fetching, verify data version (avoid new GetDCache call) */
627 if (hsame(avc->m.DataVersion, tdc->f.versionNo)) {
628 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
629 len = tdc->f.chunkBytes - offset;
632 /* don't have current data, so get it below */
634 tdc = (struct dcache *) 0;
639 ReleaseReadLock(&avc->lock);
640 tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 1);
641 ObtainReadLock(&avc->lock);
649 if (len > totalLength) len = totalLength; /* will read len bytes */
650 if (len <= 0) { /* shouldn't get here if DFFetching is on */
651 /* read past the end of a chunk, may not be at next chunk yet, and yet
652 also not at eof, so may have to supply fake zeros */
653 len = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; /* bytes left in chunk addr space */
654 if (len > totalLength) len = totalLength; /* and still within xfr request */
655 code = avc->m.Length - offset; /* and still within file */
656 if (len > code) len = code;
657 if (len > AFS_ZEROS) len = sizeof(afs_zeros); /* and in 0 buffer */
658 afsio_copy(auio, &tuio, tvec);
659 afsio_trim(&tuio, len);
660 AFS_UIOMOVE(afs_zeros, len, UIO_READ, &tuio, code);
667 /* get the data from the file */
669 if (tfile = tdc->ihint) {
670 if (tdc->f.inode != tfile->inum){
671 afs_warn( "afs_UFSRead: %x hint mismatch tdc %d inum %d\n",
672 tdc, tdc->f.inode, tfile->inum );
674 tdc->ihint = tfile = 0;
684 tfile = (struct osi_file *)osi_UFSOpen(tdc->f.inode);
685 /* mung uio structure to be right for this transfer */
686 afsio_copy(auio, &tuio, tvec);
687 afsio_trim(&tuio, len);
688 tuio.afsio_offset = offset;
692 code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, &tuio, NULL, NULL,
693 NULL, &afs_osi_cred);
697 code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, &tuio, NULL, NULL);
698 /* Flush all JFS pages now for big performance gain in big file cases
699 * If we do something like this, must check to be sure that AFS file
700 * isn't mmapped... see afs_gn_map() for why.
703 if (tfile->vnode->v_gnode && tfile->vnode->v_gnode->gn_seg) {
704 many different ways to do similar things:
705 so far, the best performing one is #2, but #1 might match it if we
706 straighten out the confusion regarding which pages to flush. It
708 1. vm_flushp(tfile->vnode->v_gnode->gn_seg, 0, len/PAGESIZE - 1);
709 2. vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE,
710 (len + PAGESIZE-1)/PAGESIZE);
711 3. vms_inactive(tfile->vnode->v_gnode->gn_seg) Doesn't work correctly
712 4. vms_delete(tfile->vnode->v_gnode->gn_seg) probably also fails
713 tfile->vnode->v_gnode->gn_seg = NULL;
717 Unfortunately, this seems to cause frequent "cache corruption" episodes.
718 vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE,
719 (len + PAGESIZE-1)/PAGESIZE);
723 code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, (off_t)&offset, &tuio, NULL, NULL, -1);
729 VOP_RWLOCK(tfile->vnode, 0);
730 code = VOP_READ(tfile->vnode, &tuio, 0, &afs_osi_cred);
731 VOP_RWUNLOCK(tfile->vnode, 0);
734 #if defined(AFS_SGI_ENV)
736 AFS_VOP_RWLOCK(tfile->vnode, VRWLOCK_READ);
737 AFS_VOP_READ(tfile->vnode, &tuio, IO_ISLOCKED, &afs_osi_cred,
739 AFS_VOP_RWUNLOCK(tfile->vnode, VRWLOCK_READ);
743 tuio.uio_rw = UIO_READ;
745 VOP_READ(tfile->vnode, &tuio, 0, &afs_osi_cred, code);
747 #else /* AFS_OSF_ENV */
749 code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, &afs_osi_cred);
751 #if defined(AFS_HPUX100_ENV)
753 code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, &afs_osi_cred);
756 #if defined(AFS_LINUX20_ENV)
758 code = osi_file_uio_rdwr(tfile, &tuio, UIO_READ);
761 #if defined(AFS_DARWIN_ENV)
763 VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, current_proc());
764 code = VOP_READ(tfile->vnode, &tuio, 0, &afs_osi_cred);
765 VOP_UNLOCK(tfile->vnode, 0, current_proc());
768 code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, &afs_osi_cred);
779 if (!tdc->ihint && nihints < maxIHint) {
792 /* otherwise we've read some, fixup length, etc and continue with next seg */
793 len = len - tuio.afsio_resid; /* compute amount really transferred */
794 afsio_skip(auio, len); /* update input uio structure */
796 transferLength += len;
798 if (len <= 0) break; /* surprise eof */
801 /* if we make it here with tdc non-zero, then it is the last chunk we
802 * dealt with, and we have to release it when we're done. We hold on
803 * to it in case we need to do a prefetch, obviously.
806 #ifndef AFS_VM_RDWR_ENV
807 /* try to queue prefetch, if needed */
808 if (!(tdc->flags & DFNextStarted) && !noLock) {
809 afs_PrefetchChunk(avc, tdc, acred, &treq);
815 ReleaseReadLock(&avc->lock);
817 osi_FreeSmallSpace(tvec);
818 error = afs_CheckCode(error, &treq, 13);