pull-prototypes-to-head-20020821
[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_UFSReadFast
15  * afs_UFSRead
16  * 
17  */
18
19 #include <afsconfig.h>
20 #include "../afs/param.h"
21
22 RCSID("$Header$");
23
24 #include "../afs/sysincludes.h" /* Standard vendor system headers */
25 #include "../afs/afsincludes.h" /* Afs-based standard headers */
26 #include "../afs/afs_stats.h" /* statistics */
27 #include "../afs/afs_cbqueue.h"
28 #include "../afs/nfsclient.h"
29 #include "../afs/afs_osidnlc.h"
30
31
32 extern char afs_zeros[AFS_ZEROS];
33
34 afs_int32 maxIHint;
35 afs_int32 nihints;                           /* # of above actually in-use */
36 afs_int32 usedihint;
37
38
39 /* Imported variables */
40 extern afs_rwlock_t afs_xdcache;
41 extern unsigned char *afs_indexFlags;
42 extern afs_hyper_t *afs_indexTimes;          /* Dcache entry Access times */
43 extern afs_hyper_t afs_indexCounter;         /* Fake time for marking index */
44
45
46 /* Forward declarations */
47 void afs_PrefetchChunk(struct vcache *avc, struct dcache *adc,
48                               struct AFS_UCRED *acred, struct vrequest *areq);
49
50 int afs_MemRead(register struct vcache *avc, struct uio *auio, struct AFS_UCRED *acred, 
51         daddr_t albn, struct buf **abpp, int noLock)
52 {
53     afs_size_t totalLength;
54     afs_size_t transferLength;
55     afs_size_t filePos;
56     afs_size_t offset, len, tlen;
57     afs_int32 trimlen;
58     struct dcache *tdc=0;
59     afs_int32 error, trybusy=1;
60     struct uio tuio;
61     struct iovec *tvec;
62     char *tfile;
63     afs_int32 code;
64     struct vrequest treq;
65
66     AFS_STATCNT(afs_MemRead);
67     if (avc->vc_error)
68         return EIO;
69
70     /* check that we have the latest status info in the vnode cache */
71     if ((code = afs_InitReq(&treq, acred))) return code;
72     if (!noLock) {
73         code = afs_VerifyVCache(avc, &treq);
74         if (code) {
75           code = afs_CheckCode(code, &treq, 8); /* failed to get it */
76           return code;
77         }
78     }
79
80 #ifndef AFS_VM_RDWR_ENV
81     if (AFS_NFSXLATORREQ(acred)) {
82         if (!afs_AccessOK(avc, PRSFS_READ, &treq,
83                           CHECK_MODE_BITS|CMB_ALLOW_EXEC_AS_READ)) {
84             return afs_CheckCode(EACCES, &treq, 9);
85         }
86     }
87 #endif
88
89     tvec = (struct iovec *) osi_AllocSmallSpace(sizeof(struct iovec));
90     totalLength = auio->afsio_resid;
91     filePos = auio->afsio_offset;
92     afs_Trace4(afs_iclSetp, CM_TRACE_READ, ICL_TYPE_POINTER, avc, 
93                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(filePos),
94                 ICL_TYPE_INT32, totalLength,
95                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length));
96     error = 0;
97     transferLength = 0;
98     if (!noLock)
99         ObtainReadLock(&avc->lock);
100 #if     defined(AFS_TEXT_ENV) && !defined(AFS_VM_RDWR_ENV)
101     if (avc->flushDV.high == AFS_MAXDV && avc->flushDV.low == AFS_MAXDV) {
102         hset(avc->flushDV, avc->m.DataVersion);
103     }
104 #endif
105
106     /*
107      * Locks held:
108      * avc->lock(R)
109      */
110     while (totalLength > 0) {
111         /* read all of the cached info */
112         if (filePos >= avc->m.Length) break;    /* all done */
113         if (noLock) {
114             if (tdc) {
115                 ReleaseReadLock(&tdc->lock);
116                 afs_PutDCache(tdc);
117             }
118             tdc = afs_FindDCache(avc, filePos);
119             if (tdc) {
120                 ObtainReadLock(&tdc->lock);
121                 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
122                 len = tdc->f.chunkBytes - offset;
123             }
124         } else {
125             /* a tricky question: does the presence of the DFFetching flag
126                mean that we're fetching the latest version of the file?  No.
127                The server could update the file as soon as the fetch responsible
128                for the setting of the DFFetching flag completes.
129             
130                However, the presence of the DFFetching flag (visible under
131                a dcache read lock since it is set and cleared only under a
132                dcache write lock) means that we're fetching as good a version
133                as was known to this client at the time of the last call to
134                afs_VerifyVCache, since the latter updates the stat cache's
135                m.DataVersion field under a vcache write lock, and from the
136                time that the DFFetching flag goes on in afs_GetDCache (before
137                the fetch starts), to the time it goes off (after the fetch
138                completes), afs_GetDCache keeps at least a read lock on the
139                vcache entry.
140             
141                This means that if the DFFetching flag is set, we can use that
142                data for any reads that must come from the current version of
143                the file (current == m.DataVersion).
144              
145                Another way of looking at this same point is this: if we're
146                fetching some data and then try do an afs_VerifyVCache, the
147                VerifyVCache operation will not complete until after the
148                DFFetching flag is turned off and the dcache entry's f.versionNo
149                field is updated.
150              
151                Note, by the way, that if DFFetching is set,
152                m.DataVersion > f.versionNo (the latter is not updated until
153                after the fetch completes).
154              */
155             if (tdc) {
156                 ReleaseReadLock(&tdc->lock);
157                 afs_PutDCache(tdc);     /* before reusing tdc */
158             }
159             tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 2);
160             ObtainReadLock(&tdc->lock);
161             /* now, first try to start transfer, if we'll need the data.  If
162              * data already coming, we don't need to do this, obviously.  Type
163              * 2 requests never return a null dcache entry, btw.
164              */
165             if (!(tdc->dflags & DFFetching)
166                 && !hsame(avc->m.DataVersion, tdc->f.versionNo)) {
167                 /* have cache entry, it is not coming in now,
168                  * and we'll need new data */
169 tagain:
170                 if (trybusy && !afs_BBusy()) {
171                     struct brequest *bp;
172                     /* daemon is not busy */
173                     ObtainSharedLock(&tdc->mflock, 665);
174                     if (!(tdc->mflags & DFFetchReq)) {
175                         /* start the daemon (may already be running, however) */
176                         UpgradeSToWLock(&tdc->mflock, 666);
177                         tdc->mflags |= DFFetchReq;
178                         bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred,
179                                         (afs_size_t)filePos, (afs_size_t) 0,
180                                         tdc);
181                         if (!bp) {
182                             tdc->mflags &= ~DFFetchReq;
183                             trybusy = 0;        /* Avoid bkg daemon since they're too busy */
184                             ReleaseWriteLock(&tdc->mflock);
185                             goto tagain;
186                         }
187                         ConvertWToSLock(&tdc->mflock);
188                         /* don't use bp pointer! */
189                     }
190                     code = 0;
191                     ConvertSToRLock(&tdc->mflock);
192                     while (!code && tdc->mflags & DFFetchReq) {
193                         /* don't need waiting flag on this one */
194                         ReleaseReadLock(&tdc->mflock);
195                         ReleaseReadLock(&tdc->lock);
196                         ReleaseReadLock(&avc->lock);
197                         code = afs_osi_SleepSig(&tdc->validPos);
198                         ObtainReadLock(&avc->lock);
199                         ObtainReadLock(&tdc->lock);
200                         ObtainReadLock(&tdc->mflock);
201                     }
202                     ReleaseReadLock(&tdc->mflock);
203                     if (code) {
204                         error = code;
205                         break;
206                     }
207                 }
208             }
209             /* now data may have started flowing in (if DFFetching is on).  If
210              * data is now streaming in, then wait for some interesting stuff.
211              */
212             code = 0;
213             while (!code && (tdc->dflags & DFFetching) &&
214                    tdc->validPos <= filePos) {
215                 /* too early: wait for DFFetching flag to vanish,
216                  * or data to appear */
217                 afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT,
218                                 ICL_TYPE_STRING, __FILE__,
219                                 ICL_TYPE_INT32, __LINE__,
220                                 ICL_TYPE_POINTER, tdc,
221                                 ICL_TYPE_INT32, tdc->dflags);
222                 ReleaseReadLock(&tdc->lock);
223                 ReleaseReadLock(&avc->lock);
224                 code = afs_osi_SleepSig(&tdc->validPos);
225                 ObtainReadLock(&avc->lock);
226                 ObtainReadLock(&tdc->lock);
227             }
228             if (code) {
229                 error = code;
230                 break;
231             }
232             /* fetching flag gone, data is here, or we never tried (BBusy for instance) */
233             if (tdc->dflags & DFFetching) {
234                 /* still fetching, some new data is here: compute length and offset */
235                 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
236                 len = tdc->validPos - filePos;
237             }
238             else {
239                 /* no longer fetching, verify data version (avoid new GetDCache call) */
240                 if (hsame(avc->m.DataVersion, tdc->f.versionNo)) {
241                     offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
242                     len = tdc->f.chunkBytes - offset;
243                 }
244                 else {
245                     /* don't have current data, so get it below */
246                     ReleaseReadLock(&tdc->lock);
247                     afs_PutDCache(tdc);
248                     tdc = NULL;
249                 }
250             }
251
252             if (!tdc) {
253                 ReleaseReadLock(&avc->lock);
254                 tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 1);
255                 ObtainReadLock(&avc->lock);
256                 if (tdc) ObtainReadLock(&tdc->lock);
257             }
258         }
259
260         if (!tdc) {
261             error = EIO;
262             break;
263         }
264
265         /*
266          * Locks held:
267          * avc->lock(R)
268          * tdc->lock(R)
269          */
270
271         if (len > totalLength) len = totalLength;   /* will read len bytes */
272         if (len <= 0) { /* shouldn't get here if DFFetching is on */
273             /* read past the end of a chunk, may not be at next chunk yet, and yet
274                 also not at eof, so may have to supply fake zeros */
275             len = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; /* bytes left in chunk addr space */
276             if (len > totalLength) len = totalLength;   /* and still within xfr request */
277             tlen = avc->m.Length - offset; /* and still within file */ 
278             if (len > tlen) len = tlen;
279             if (len > AFS_ZEROS) len = sizeof(afs_zeros);   /* and in 0 buffer */
280             afsio_copy(auio, &tuio, tvec);
281             trimlen = len;
282             afsio_trim(&tuio, trimlen);
283             AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, &tuio, code);
284             if (code) {
285                 error = code;
286                 break;
287             }
288         }
289         else {
290             /* get the data from the mem cache */
291
292             /* mung uio structure to be right for this transfer */
293             afsio_copy(auio, &tuio, tvec);
294             trimlen = len;
295             afsio_trim(&tuio, trimlen);
296             tuio.afsio_offset = offset;
297
298             code = afs_MemReadUIO(tdc->f.inode, &tuio);
299
300             if (code) {
301                 error = code;
302                 break;
303             }
304         }
305         /* otherwise we've read some, fixup length, etc and continue with next seg */
306         len = len - tuio.afsio_resid; /* compute amount really transferred */
307         trimlen = len;
308         afsio_skip(auio, trimlen);          /* update input uio structure */
309         totalLength -= len;
310         transferLength += len;
311         filePos += len;
312
313         if (len <= 0) break;    /* surprise eof */
314     }   /* the whole while loop */
315
316     /*
317      * Locks held:
318      * avc->lock(R)
319      * tdc->lock(R) if tdc
320      */
321
322     /* if we make it here with tdc non-zero, then it is the last chunk we
323      * dealt with, and we have to release it when we're done.  We hold on
324      * to it in case we need to do a prefetch.
325      */
326     if (tdc) {
327         ReleaseReadLock(&tdc->lock);
328 #if !defined(AFS_VM_RDWR_ENV)
329         /* try to queue prefetch, if needed */
330         if (!noLock) {
331             afs_PrefetchChunk(avc, tdc, acred, &treq);
332         }
333 #endif
334         afs_PutDCache(tdc);
335     }
336     if (!noLock)
337         ReleaseReadLock(&avc->lock);
338     osi_FreeSmallSpace(tvec);
339     error = afs_CheckCode(error, &treq, 10);
340     return error;
341 }
342
343 /* called with the dcache entry triggering the fetch, the vcache entry involved,
344  * and a vrequest for the read call.  Marks the dcache entry as having already
345  * triggered a prefetch, starts the prefetch going and sets the DFFetchReq
346  * flag in the prefetched block, so that the next call to read knows to wait
347  * for the daemon to start doing things.
348  *
349  * This function must be called with the vnode at least read-locked, and
350  * no locks on the dcache, because it plays around with dcache entries.
351  */
352 void afs_PrefetchChunk(struct vcache *avc, struct dcache *adc,
353                               struct AFS_UCRED *acred, struct vrequest *areq)
354 {
355     register struct dcache *tdc;
356     afs_size_t offset;
357     afs_size_t j1, j2;  /* junk vbls for GetDCache to trash */
358
359     offset = adc->f.chunk+1;            /* next chunk we'll need */
360     offset = AFS_CHUNKTOBASE(offset);   /* base of next chunk */
361     ObtainReadLock(&adc->lock);
362     ObtainSharedLock(&adc->mflock, 662);
363     if (offset < avc->m.Length && !(adc->mflags & DFNextStarted) && !afs_BBusy()) {
364         struct brequest *bp;
365
366         UpgradeSToWLock(&adc->mflock, 663);
367         adc->mflags |= DFNextStarted;   /* we've tried to prefetch for this guy */
368         ReleaseWriteLock(&adc->mflock);
369         ReleaseReadLock(&adc->lock);
370
371         tdc = afs_GetDCache(avc, offset, areq, &j1, &j2, 2);    /* type 2 never returns 0 */
372         ObtainSharedLock(&tdc->mflock, 651);
373         if (!(tdc->mflags & DFFetchReq)) {
374             /* ask the daemon to do the work */
375             UpgradeSToWLock(&tdc->mflock, 652);
376             tdc->mflags |= DFFetchReq;  /* guaranteed to be cleared by BKG or GetDCache */
377             /* last parm (1) tells bkg daemon to do an afs_PutDCache when it is done,
378              * since we don't want to wait for it to finish before doing so ourselves.
379              */
380             bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred,
381                             (afs_size_t) offset, (afs_size_t) 1, tdc);
382             if (!bp) {
383                 /* Bkg table full; just abort non-important prefetching to avoid deadlocks */
384                 tdc->mflags &= ~DFFetchReq;
385                 ReleaseWriteLock(&tdc->mflock);
386                 afs_PutDCache(tdc);
387
388                 /*
389                  * DCLOCKXXX: This is a little sketchy, since someone else
390                  * could have already started a prefetch..  In practice,
391                  * this probably doesn't matter; at most it would cause an
392                  * extra slot in the BKG table to be used up when someone
393                  * prefetches this for the second time.
394                  */
395                 ObtainReadLock(&adc->lock);
396                 ObtainWriteLock(&adc->mflock, 664);
397                 adc->mflags &= ~DFNextStarted;
398                 ReleaseWriteLock(&adc->mflock);
399                 ReleaseReadLock(&adc->lock);
400             } else {
401                 ReleaseWriteLock(&tdc->mflock);
402             }
403         } else {
404             ReleaseSharedLock(&tdc->mflock);
405             afs_PutDCache(tdc);
406         }
407     } else {
408         ReleaseSharedLock(&adc->mflock);
409         ReleaseReadLock(&adc->lock);
410     }
411 }
412
413
414 /* if the vcache is up-to-date, and the request fits entirely into the chunk
415  * that the hint here references, then we just use it quickly, otherwise we
416  * have to call the slow read.
417  *
418  * This could be generalized in several ways to take advantage of partial 
419  * state even when all the chips don't fall the right way.  For instance,
420  * if the hint is good and the first part of the read request can be 
421  * satisfied from the chunk, then just do the read.  After the read has
422  * completed, check to see if there's more. (Chances are there won't be.)
423  * If there is more, then just call afs_UFSReadSlow and let it do the rest.
424  *
425  * For the time being, I'm ignoring quick.f, but it should be used at
426  * some future date.
427  * do this in the future avc->quick.f = tfile; but I think it
428  * has to be done under a write lock, but don't want to wait on the
429  * write lock
430  */
431     /* everywhere that a dcache can be freed (look for NULLIDX) 
432      * probably does it under a write lock on xdcache.  Need to invalidate
433      * stamp there, too.  
434      * Also need to worry about DFFetching, and IFFree, I think. */
435 static struct dcache *savedc = 0;
436
437 int afs_UFSReadFast(register struct vcache *avc, struct uio *auio, 
438         struct AFS_UCRED *acred, daddr_t albn, struct buf **abpp, int noLock)
439 {
440     struct vrequest treq;
441     int offDiff;
442     struct dcache *tdc;
443     struct vnode *vp;
444     struct osi_file *tfile;
445     afs_int32 code = 0;
446
447     if (!noLock)
448         ObtainReadLock(&avc->lock);  
449     ObtainReadLock(&afs_xdcache);
450
451     if ((avc->states & CStatd)                                /* up to date */
452         && (tdc = avc->quick.dc) && (tdc->index != NULLIDX) 
453         && !(afs_indexFlags[tdc->index] & (IFFree | IFDiscarded)))   {
454
455         int readLocked = 0;
456
457         afs_RefDCache(tdc);
458         ReleaseReadLock(&afs_xdcache);
459         if (tdc->stamp == avc->quick.stamp) {
460             readLocked = 1;
461             ObtainReadLock(&tdc->lock);
462         }
463
464         if ((tdc->stamp == avc->quick.stamp)                /* hint matches */
465             && ((offDiff = (auio->afsio_offset - avc->quick.minLoc)) >= 0)
466             && (tdc->f.chunkBytes >= auio->afsio_resid + offDiff)
467             && !(tdc->dflags & DFFetching)) { /* fits in chunk */
468
469             auio->afsio_offset -= avc->quick.minLoc;
470
471             afs_Trace4(afs_iclSetp, CM_TRACE_READFAST, ICL_TYPE_POINTER, avc,
472                         ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(auio->afsio_offset),
473                         ICL_TYPE_INT32, auio->afsio_resid,
474                         ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length));
475
476             tfile = (struct osi_file *)osi_UFSOpen(tdc->f.inode);
477
478 #ifdef  AFS_AIX_ENV
479 #ifdef  AFS_AIX41_ENV
480             AFS_GUNLOCK();
481             code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, auio, NULL, NULL, NULL, &afs_osi_cred);
482             AFS_GLOCK();
483 #else
484 #ifdef AFS_AIX32_ENV
485             code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, auio, NULL, NULL);
486 #else
487             code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, (off_t)&auio->afsio_offset, auio, NULL, NULL, -1);
488 #endif
489 #endif
490 #else
491 #ifdef  AFS_SUN5_ENV
492             AFS_GUNLOCK();
493             VOP_RWLOCK(tfile->vnode, 0);
494             code = VOP_READ(tfile->vnode, auio, 0, &afs_osi_cred);
495             VOP_RWUNLOCK(tfile->vnode, 0);
496             AFS_GLOCK();
497 #else
498 #if defined(AFS_SGI_ENV)
499             AFS_GUNLOCK();
500             AFS_VOP_RWLOCK(tfile->vnode, VRWLOCK_READ);
501             AFS_VOP_READ(tfile->vnode, auio, IO_ISLOCKED, &afs_osi_cred, code);
502             AFS_VOP_RWUNLOCK(tfile->vnode, VRWLOCK_READ);
503             AFS_GLOCK();
504 #else
505 #ifdef  AFS_OSF_ENV
506             auio->uio_rw = UIO_READ;
507             AFS_GUNLOCK();
508             VOP_READ(tfile->vnode, auio, 0, &afs_osi_cred, code);
509             AFS_GLOCK();
510 #else   /* AFS_OSF_ENV */
511 #if defined(AFS_HPUX100_ENV)
512             AFS_GUNLOCK();
513             code = VOP_RDWR(tfile->vnode, auio, UIO_READ, 0, &afs_osi_cred);
514             AFS_GLOCK();
515 #else
516 #if defined(AFS_LINUX20_ENV)
517             AFS_GUNLOCK();
518             code = osi_file_uio_rdwr(tfile, auio, UIO_READ);
519             AFS_GLOCK();
520 #else
521 #if defined(AFS_DARWIN_ENV)
522             AFS_GUNLOCK();
523             VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, current_proc());
524             code = VOP_READ(tfile->vnode, auio, 0, &afs_osi_cred);
525             VOP_UNLOCK(tfile->vnode, 0, current_proc());
526             AFS_GLOCK();
527 #else
528 #if defined(AFS_FBSD_ENV)
529             AFS_GUNLOCK();
530             VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, curproc);
531             code = VOP_READ(tfile->vnode, auio, 0, &afs_osi_cred);
532             VOP_UNLOCK(tfile->vnode, 0, curproc);
533             AFS_GLOCK();
534 #else
535             code = VOP_RDWR(tfile->vnode, auio, UIO_READ, 0, &afs_osi_cred);
536 #endif
537 #endif
538 #endif
539 #endif
540 #endif
541 #endif
542 #endif
543 #endif
544             auio->afsio_offset += avc->quick.minLoc;
545             osi_UFSClose(tfile);
546             /* Fix up LRU info */
547             hset(afs_indexTimes[tdc->index], afs_indexCounter);
548             hadd32(afs_indexCounter, 1);
549
550             if (!noLock) {
551                 ReleaseReadLock(&avc->lock);
552 #if !defined(AFS_VM_RDWR_ENV)
553                 if (!(code = afs_InitReq(&treq, acred))) {
554                     if (!(tdc->mflags & DFNextStarted))
555                         afs_PrefetchChunk(avc, tdc, acred, &treq);
556                 }
557 #endif
558             }
559             if (readLocked) ReleaseReadLock(&tdc->lock);
560             afs_PutDCache(tdc);
561             return (code);
562         }
563         if (!tdc->f.chunkBytes) {   /* debugging f.chunkBytes == 0 problem */
564             savedc = tdc;
565         }
566         if (readLocked) ReleaseReadLock(&tdc->lock);
567         afs_PutDCache(tdc);
568     } else {
569         ReleaseReadLock(&afs_xdcache);
570     }
571
572     /* come here if fast path doesn't work for some reason or other */
573     if (!noLock)
574         ReleaseReadLock(&avc->lock);
575     return afs_UFSRead(avc, auio, acred, albn, abpp, noLock);
576 }
577
578 int afs_UFSRead(register struct vcache *avc, struct uio *auio,
579         struct AFS_UCRED *acred, daddr_t albn, struct buf **abpp, int noLock)
580 {
581     afs_size_t totalLength;
582     afs_size_t transferLength;
583     afs_size_t filePos;
584     afs_size_t offset, len, tlen;
585     afs_int32 trimlen;
586     struct dcache *tdc=0;
587     afs_int32 error;
588     struct uio tuio;
589     struct iovec *tvec;
590     struct osi_file *tfile;
591     afs_int32 code;
592     int trybusy=1;
593     struct vnode *vp;
594     struct vrequest treq;
595
596     AFS_STATCNT(afs_UFSRead);
597     if (avc && avc->vc_error)
598         return EIO;
599
600     /* check that we have the latest status info in the vnode cache */
601     if ((code = afs_InitReq(&treq, acred))) return code;
602     if (!noLock) {
603       if (!avc) 
604         osi_Panic ("null avc in afs_UFSRead");
605       else {
606         code = afs_VerifyVCache(avc, &treq);
607         if (code) {
608           code = afs_CheckCode(code, &treq, 11); /* failed to get it */
609           return code;
610         }
611       }
612     }
613
614 #ifndef AFS_VM_RDWR_ENV
615     if (AFS_NFSXLATORREQ(acred)) {
616         if (!afs_AccessOK(avc, PRSFS_READ, &treq,
617                           CHECK_MODE_BITS|CMB_ALLOW_EXEC_AS_READ)) {
618             return afs_CheckCode(EACCES, &treq, 12);
619         }
620     }
621 #endif
622
623     tvec = (struct iovec *) osi_AllocSmallSpace(sizeof(struct iovec));
624     totalLength = auio->afsio_resid;
625     filePos = auio->afsio_offset;
626     afs_Trace4(afs_iclSetp, CM_TRACE_READ, ICL_TYPE_POINTER, avc, 
627                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(filePos),
628                 ICL_TYPE_INT32, totalLength,
629                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length));
630     error = 0;
631     transferLength = 0;
632     if (!noLock)
633         ObtainReadLock(&avc->lock);
634 #if     defined(AFS_TEXT_ENV) && !defined(AFS_VM_RDWR_ENV)
635     if (avc->flushDV.high == AFS_MAXDV && avc->flushDV.low == AFS_MAXDV) {
636         hset(avc->flushDV, avc->m.DataVersion);
637     }
638 #endif
639     
640     while (totalLength > 0) {
641         /* read all of the cached info */
642         if (filePos >= avc->m.Length) break;    /* all done */
643         if (noLock) {
644             if (tdc) {
645                 ReleaseReadLock(&tdc->lock);
646                 afs_PutDCache(tdc);
647             }
648             tdc = afs_FindDCache(avc, filePos);
649             if (tdc) {
650                 ObtainReadLock(&tdc->lock);
651                 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
652                 len = tdc->f.chunkBytes - offset;
653             }
654         } else {
655             /* a tricky question: does the presence of the DFFetching flag
656                mean that we're fetching the latest version of the file?  No.
657                The server could update the file as soon as the fetch responsible
658                for the setting of the DFFetching flag completes.
659             
660                However, the presence of the DFFetching flag (visible under
661                a dcache read lock since it is set and cleared only under a
662                dcache write lock) means that we're fetching as good a version
663                as was known to this client at the time of the last call to
664                afs_VerifyVCache, since the latter updates the stat cache's
665                m.DataVersion field under a vcache write lock, and from the
666                time that the DFFetching flag goes on in afs_GetDCache (before
667                the fetch starts), to the time it goes off (after the fetch
668                completes), afs_GetDCache keeps at least a read lock on the
669                vcache entry.
670             
671                This means that if the DFFetching flag is set, we can use that
672                data for any reads that must come from the current version of
673                the file (current == m.DataVersion).
674              
675                Another way of looking at this same point is this: if we're
676                fetching some data and then try do an afs_VerifyVCache, the
677                VerifyVCache operation will not complete until after the
678                DFFetching flag is turned off and the dcache entry's f.versionNo
679                field is updated.
680              
681                Note, by the way, that if DFFetching is set,
682                m.DataVersion > f.versionNo (the latter is not updated until
683                after the fetch completes).
684              */
685             if (tdc) {
686                 ReleaseReadLock(&tdc->lock);
687                 afs_PutDCache(tdc);     /* before reusing tdc */
688             }
689             tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 2);
690             ObtainReadLock(&tdc->lock);
691             if (tdc == savedc) {
692                 savedc = 0;
693             }
694             /* now, first try to start transfer, if we'll need the data.  If
695              * data already coming, we don't need to do this, obviously.  Type
696              * 2 requests never return a null dcache entry, btw. */
697             if (!(tdc->dflags & DFFetching)
698                 && !hsame(avc->m.DataVersion, tdc->f.versionNo)) {
699                 /* have cache entry, it is not coming in now, and we'll need new data */
700 tagain:
701                 if (trybusy && !afs_BBusy()) {
702                     struct brequest *bp;
703                     /* daemon is not busy */
704                     ObtainSharedLock(&tdc->mflock, 667);
705                     if (!(tdc->mflags & DFFetchReq)) {
706                         UpgradeSToWLock(&tdc->mflock, 668);
707                         tdc->mflags |= DFFetchReq;
708                         bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred,
709                                         (afs_size_t) filePos, (afs_size_t) 0,
710                                         tdc);
711                         if (!bp) {
712                             /* Bkg table full; retry deadlocks */
713                             tdc->mflags &= ~DFFetchReq;
714                             trybusy = 0;        /* Avoid bkg daemon since they're too busy */
715                             ReleaseWriteLock(&tdc->mflock);
716                             goto tagain;
717                         }
718                         ConvertWToSLock(&tdc->mflock);
719                     }
720                     code = 0;
721                     ConvertSToRLock(&tdc->mflock);
722                     while (!code && tdc->mflags & DFFetchReq) {
723                         /* don't need waiting flag on this one */
724                         ReleaseReadLock(&tdc->mflock);
725                         ReleaseReadLock(&tdc->lock);
726                         ReleaseReadLock(&avc->lock);
727                         code = afs_osi_SleepSig(&tdc->validPos);
728                         ObtainReadLock(&avc->lock);
729                         ObtainReadLock(&tdc->lock);
730                         ObtainReadLock(&tdc->mflock);
731                     }
732                     ReleaseReadLock(&tdc->mflock);
733                     if (code) {
734                         error = code;
735                         break;
736                     }
737                 }
738             }
739             /* now data may have started flowing in (if DFFetching is on).  If
740              * data is now streaming in, then wait for some interesting stuff.
741              */
742             code = 0;
743             while (!code && (tdc->dflags & DFFetching) &&
744                    tdc->validPos <= filePos) {
745                 /* too early: wait for DFFetching flag to vanish,
746                  * or data to appear */
747                 afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT,
748                                 ICL_TYPE_STRING, __FILE__,
749                                 ICL_TYPE_INT32, __LINE__,
750                                 ICL_TYPE_POINTER, tdc,
751                                 ICL_TYPE_INT32, tdc->dflags);
752                 ReleaseReadLock(&tdc->lock);
753                 ReleaseReadLock(&avc->lock);
754                 code = afs_osi_SleepSig(&tdc->validPos);
755                 ObtainReadLock(&avc->lock);
756                 ObtainReadLock(&tdc->lock);
757             }
758             if (code) {
759                 error = code;
760                 break;
761             }
762             /* fetching flag gone, data is here, or we never tried
763              * (BBusy for instance) */
764             if (tdc->dflags & DFFetching) {
765                 /* still fetching, some new data is here:
766                  * compute length and offset */
767                 offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
768                 len = tdc->validPos - filePos;
769             }
770             else {
771                 /* no longer fetching, verify data version (avoid new
772                  * GetDCache call) */
773                 if (hsame(avc->m.DataVersion, tdc->f.versionNo)) {
774                     offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
775                     len = tdc->f.chunkBytes - offset;
776                 }
777                 else {
778                     /* don't have current data, so get it below */
779                     ReleaseReadLock(&tdc->lock);
780                     afs_PutDCache(tdc);
781                     tdc = NULL;
782                 }
783             }
784
785             if (!tdc) {
786                 ReleaseReadLock(&avc->lock);
787                 tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 1);
788                 ObtainReadLock(&avc->lock);
789                 if (tdc) ObtainReadLock(&tdc->lock);
790             }
791         }
792         
793         afs_Trace3(afs_iclSetp, CM_TRACE_VNODEREAD,
794                         ICL_TYPE_POINTER, tdc,
795                         ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(offset),
796                         ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(len));
797         if (!tdc) {
798             error = EIO;
799             break;
800         }
801         if (len > totalLength) len = totalLength;   /* will read len bytes */
802         if (len <= 0) { /* shouldn't get here if DFFetching is on */
803             /* read past the end of a chunk, may not be at next chunk yet, and yet
804                 also not at eof, so may have to supply fake zeros */
805             len = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; /* bytes left in chunk addr space */
806             if (len > totalLength) len = totalLength;   /* and still within xfr request */
807             tlen = avc->m.Length - offset; /* and still within file */
808             if (len > tlen) len = tlen;
809             if (len > AFS_ZEROS) len = sizeof(afs_zeros);   /* and in 0 buffer */
810             afsio_copy(auio, &tuio, tvec);
811             trimlen = len;
812             afsio_trim(&tuio, trimlen);
813             AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, &tuio, code);
814             if (code) {
815                 error = code;
816                 break;
817             }
818         }
819         else {
820             /* get the data from the file */
821 #ifdef IHINT
822           if (tfile = tdc->ihint) {
823              if (tdc->f.inode != tfile->inum){ 
824                     afs_warn( "afs_UFSRead: %x hint mismatch tdc %d inum %d\n",
825                         tdc, tdc->f.inode, tfile->inum );
826                     osi_UFSClose(tfile);
827                     tdc->ihint = tfile = 0;
828                     nihints--;
829                   }
830            }
831           if (tfile != 0) {
832             usedihint++;
833           }
834           else
835 #endif /* IHINT */
836
837             tfile = (struct osi_file *)osi_UFSOpen(tdc->f.inode);
838             /* mung uio structure to be right for this transfer */
839             afsio_copy(auio, &tuio, tvec);
840             trimlen = len;
841             afsio_trim(&tuio, trimlen);
842             tuio.afsio_offset = offset;
843 #ifdef  AFS_AIX_ENV
844 #ifdef  AFS_AIX41_ENV
845           AFS_GUNLOCK();
846           code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, &tuio, NULL, NULL,
847                            NULL, &afs_osi_cred);
848           AFS_GLOCK();
849 #else
850 #ifdef AFS_AIX32_ENV
851             code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, &tuio, NULL, NULL);
852           /* Flush all JFS pages now for big performance gain in big file cases
853            * If we do something like this, must check to be sure that AFS file 
854            * isn't mmapped... see afs_gn_map() for why.
855           */
856 /*
857           if (tfile->vnode->v_gnode && tfile->vnode->v_gnode->gn_seg) {
858  many different ways to do similar things:
859    so far, the best performing one is #2, but #1 might match it if we
860    straighten out the confusion regarding which pages to flush.  It 
861    really does matter.
862    1.       vm_flushp(tfile->vnode->v_gnode->gn_seg, 0, len/PAGESIZE - 1);
863    2.       vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE, 
864                         (len + PAGESIZE-1)/PAGESIZE);
865    3.       vms_inactive(tfile->vnode->v_gnode->gn_seg) Doesn't work correctly
866    4.       vms_delete(tfile->vnode->v_gnode->gn_seg) probably also fails
867             tfile->vnode->v_gnode->gn_seg = NULL;
868    5.       deletep
869    6.       ipgrlse
870    7.       ifreeseg
871           Unfortunately, this seems to cause frequent "cache corruption" episodes.
872             vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE, 
873                         (len + PAGESIZE-1)/PAGESIZE);
874           }     
875 */
876 #else
877             code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, (off_t)&offset, &tuio, NULL, NULL, -1);
878 #endif
879 #endif
880 #else
881 #ifdef  AFS_SUN5_ENV
882           AFS_GUNLOCK();
883             VOP_RWLOCK(tfile->vnode, 0);
884             code = VOP_READ(tfile->vnode, &tuio, 0, &afs_osi_cred);
885             VOP_RWUNLOCK(tfile->vnode, 0);
886           AFS_GLOCK();
887 #else
888 #if defined(AFS_SGI_ENV)
889             AFS_GUNLOCK();
890             AFS_VOP_RWLOCK(tfile->vnode, VRWLOCK_READ);
891             AFS_VOP_READ(tfile->vnode, &tuio, IO_ISLOCKED, &afs_osi_cred,
892                          code);
893             AFS_VOP_RWUNLOCK(tfile->vnode, VRWLOCK_READ);
894             AFS_GLOCK();
895 #else
896 #ifdef  AFS_OSF_ENV
897             tuio.uio_rw = UIO_READ;
898             AFS_GUNLOCK();
899             VOP_READ(tfile->vnode, &tuio, 0, &afs_osi_cred, code);
900             AFS_GLOCK();
901 #else   /* AFS_OSF_ENV */
902 #ifdef AFS_SUN_ENV
903             code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, &afs_osi_cred);
904 #else
905 #if     defined(AFS_HPUX100_ENV)
906             AFS_GUNLOCK();
907             code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, &afs_osi_cred);
908             AFS_GLOCK();
909 #else
910 #if defined(AFS_LINUX20_ENV)
911             AFS_GUNLOCK();
912             code = osi_file_uio_rdwr(tfile, &tuio, UIO_READ);
913             AFS_GLOCK();
914 #else
915 #if defined(AFS_DARWIN_ENV)
916             AFS_GUNLOCK();
917             VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, current_proc());
918             code = VOP_READ(tfile->vnode, &tuio, 0, &afs_osi_cred);
919             VOP_UNLOCK(tfile->vnode, 0, current_proc());
920             AFS_GLOCK();
921 #else
922 #if defined(AFS_FBSD_ENV)
923             AFS_GUNLOCK();
924             VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, curproc);
925             code = VOP_READ(tfile->vnode, &tuio, 0, &afs_osi_cred);
926             VOP_UNLOCK(tfile->vnode, 0, curproc);
927             AFS_GLOCK();
928 #else
929             code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, &afs_osi_cred);
930 #endif
931 #endif
932 #endif
933 #endif
934 #endif
935 #endif
936 #endif
937 #endif
938 #endif
939
940 #ifdef IHINT
941              if (!tdc->ihint && nihints < maxIHint) {
942                tdc->ihint = tfile;
943                nihints++;
944              }
945              else
946 #endif /* IHINT */
947                osi_UFSClose(tfile);
948
949             if (code) {
950                 error = code;
951                 break;
952             }
953         }
954         /* otherwise we've read some, fixup length, etc and continue with next seg */
955         len = len - tuio.afsio_resid; /* compute amount really transferred */
956         trimlen = len;
957         afsio_skip(auio, trimlen);          /* update input uio structure */
958         totalLength -= len;
959         transferLength += len;
960         filePos += len;
961         if (len <= 0) break;    /* surprise eof */
962     }
963
964     /* if we make it here with tdc non-zero, then it is the last chunk we
965      * dealt with, and we have to release it when we're done.  We hold on
966      * to it in case we need to do a prefetch, obviously.
967      */
968     if (tdc) {
969         ReleaseReadLock(&tdc->lock);
970 #if !defined(AFS_VM_RDWR_ENV)
971         /* try to queue prefetch, if needed */
972         if (!noLock) {
973             if (!(tdc->mflags & DFNextStarted))
974                 afs_PrefetchChunk(avc, tdc, acred, &treq);
975         }
976 #endif
977         afs_PutDCache(tdc);
978     }
979     if (!noLock)
980         ReleaseReadLock(&avc->lock);
981
982     osi_FreeSmallSpace(tvec);
983     error = afs_CheckCode(error, &treq, 13);
984     return error;
985 }