69a08cdf77a06ab8121fd18d246f99706b2e855b
[openafs.git] / src / WINNT / afsd / cm_dcache.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 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #include <nb30.h>
17 #endif /* !DJGPP */
18 #include <malloc.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <osi.h>
22
23 #include "afsd.h"
24
25 osi_mutex_t cm_bufGetMutex;
26
27 /* functions called back from the buffer package when reading or writing data,
28  * or when holding or releasing a vnode pointer.
29  */
30 long cm_BufWrite(void *vfidp, osi_hyper_t *offsetp, long length, long flags,
31         cm_user_t *userp, cm_req_t *reqp)
32 {
33         /* store the data back from this buffer; the buffer is locked and held,
34          * but the vnode involved isn't locked, yet.  It is held by its
35          * reference from the buffer, which won't change until the buffer is
36          * released by our caller.  Thus, we don't have to worry about holding
37          * bufp->scp.
38          */
39         long code;
40         cm_fid_t *fidp = vfidp;
41         cm_scache_t *scp;
42         long nbytes;
43         long temp;
44         AFSFetchStatus outStatus;
45         AFSStoreStatus inStatus;
46         osi_hyper_t thyper;
47         AFSVolSync volSync;
48         AFSFid tfid;
49         struct rx_call *callp;
50         osi_queueData_t *qdp;
51         cm_buf_t *bufp;
52         long wbytes;
53         char *bufferp;
54         cm_conn_t *connp;
55         long truncPos;
56         cm_bulkIO_t biod;               /* bulk IO descriptor */
57
58         osi_assert(userp != NULL);
59
60         /* now, the buffer may or may not be filled with good data (buf_GetNew
61          * drops lots of locks, and may indeed return a properly initialized
62          * buffer, although more likely it will just return a new, empty, buffer.
63          */
64         scp = cm_FindSCache(fidp);
65         if (scp == NULL)
66                 return CM_ERROR_NOSUCHFILE;     /* shouldn't happen */
67
68         cm_AFSFidFromFid(&tfid, fidp);
69
70         lock_ObtainMutex(&scp->mx);
71         
72         code = cm_SetupStoreBIOD(scp, offsetp, length, &biod, userp, reqp);
73         if (code) {
74                 osi_Log1(afsd_logp, "cm_SetupStoreBIOD code %x", code);
75                 lock_ReleaseMutex(&scp->mx);
76                 cm_ReleaseSCache(scp);
77                 return code;
78         }
79
80         if (biod.length == 0) {
81                 osi_Log0(afsd_logp, "cm_SetupStoreBIOD length 0");
82                 lock_ReleaseMutex(&scp->mx);
83                 cm_ReleaseBIOD(&biod, 1);       /* should be a NOOP */
84                 cm_ReleaseSCache(scp);
85                 return 0;
86         }
87
88         /* Serialize StoreData RPC's; for rationale see cm_scache.c */
89         (void) cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA_EXCL);
90
91         /* prepare the output status for the store */
92         scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
93         cm_StatusFromAttr(&inStatus, scp, NULL);
94         truncPos = scp->length.LowPart;
95         if ((scp->mask & CM_SCACHEMASK_TRUNCPOS)
96                 && scp->truncPos.LowPart < (unsigned long) truncPos)
97                         truncPos = scp->truncPos.LowPart;
98         scp->mask &= ~CM_SCACHEMASK_TRUNCPOS;
99                 
100         /* compute how many bytes to write from this buffer */
101         thyper = LargeIntegerSubtract(scp->length, biod.offset);
102         if (LargeIntegerLessThanZero(thyper)) {
103                 /* entire buffer is past EOF */
104                 nbytes = 0;
105         }
106         else {
107                 /* otherwise write out part of buffer before EOF, but not
108                  * more than bufferSize bytes.
109                  */
110                 nbytes = thyper.LowPart;
111                 if (nbytes > biod.length) nbytes = biod.length;
112         }
113
114         lock_ReleaseMutex(&scp->mx);
115         
116         /* now we're ready to do the store operation */
117         do {
118                 code = cm_Conn(&scp->fid, userp, reqp, &connp);
119                 if (code) continue;
120                 
121                 callp = rx_NewCall(connp->callp);
122
123                 osi_Log3(afsd_logp, "CALL StoreData vp %x, off 0x%x, size 0x%x",
124                         (long) scp, biod.offset.LowPart, nbytes);
125
126                 code = StartRXAFS_StoreData(callp, &tfid, &inStatus,
127                         biod.offset.LowPart, nbytes, truncPos);
128
129                 if (code == 0) {
130                         /* write the data from the the list of buffers */
131                         qdp = NULL;
132                         while(nbytes > 0) {
133                                 if (qdp == NULL)
134                                         qdp = biod.bufListEndp;
135                                 else
136                                         qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
137                                 osi_assert(qdp != NULL);
138                                 bufp = osi_GetQData(qdp);
139                                 bufferp = bufp->datap;
140                                 wbytes = nbytes;
141                                 if (wbytes > buf_bufferSize) wbytes = buf_bufferSize;
142
143                                 /* write out wbytes of data from bufferp */
144                                 temp = rx_Write(callp, bufferp, wbytes);
145                                 if (temp != wbytes) {
146                                         code = -1;
147                                         break;
148                                 }
149                                 nbytes -= wbytes;
150                         }       /* while more bytes to write */
151                 }               /* if RPC started successfully */
152
153                 if (code == 0)
154                         code = EndRXAFS_StoreData(callp, &outStatus, &volSync);
155                 code = rx_EndCall(callp, code);
156                 osi_Log0(afsd_logp, "CALL StoreData DONE");
157                 
158         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, code));
159         code = cm_MapRPCError(code, reqp);
160         
161         /* now, clean up our state */
162         lock_ObtainMutex(&scp->mx);
163
164         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STOREDATA_EXCL);
165
166         if (code == 0) {
167                 /* now, here's something a little tricky: in AFS 3, a dirty
168                  * length can't be directly stored, instead, a dirty chunk is
169                  * stored that sets the file's size (by writing and by using
170                  * the truncate-first option in the store call).
171                  *
172                  * At this point, we've just finished a store, and so the trunc
173                  * pos field is clean.  If the file's size at the server is at
174                  * least as big as we think it should be, then we turn off the
175                  * length dirty bit, since all the other dirty buffers must
176                  * precede this one in the file.
177                  *
178                  * The file's desired size shouldn't be smaller than what's
179                  * stored at the server now, since we just did the trunc pos
180                  * store.
181                  *
182                  * We have to turn off the length dirty bit as soon as we can,
183                  * so that we see updates made by other machines.
184                  */
185                 if (outStatus.Length >= scp->length.LowPart)
186                         scp->mask &= ~CM_SCACHEMASK_LENGTH;
187                 cm_MergeStatus(scp, &outStatus, &volSync, userp, 0);
188         } else {
189                 if (code == CM_ERROR_SPACE)
190                         scp->flags |= CM_SCACHEFLAG_OUTOFSPACE;
191                 else if (code == CM_ERROR_QUOTA)
192                         scp->flags |= CM_SCACHEFLAG_OVERQUOTA;
193         }
194         lock_ReleaseMutex(&scp->mx);
195         cm_ReleaseBIOD(&biod, 1);
196         cm_ReleaseSCache(scp);
197
198         return code;
199 }
200
201 /*
202  * Truncate the file, by sending a StoreData RPC with zero length.
203  *
204  * Called with scp locked.  Releases and re-obtains the lock.
205  */
206 long cm_StoreMini(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
207 {
208         AFSFetchStatus outStatus;
209         AFSStoreStatus inStatus;
210         AFSVolSync volSync;
211         AFSFid tfid;
212         long code;
213         long truncPos;
214         cm_conn_t *connp;
215         struct rx_call *callp;
216
217         /* Serialize StoreData RPC's; for rationale see cm_scache.c */
218         (void) cm_SyncOp(scp, NULL, userp, reqp, 0,
219                          CM_SCACHESYNC_STOREDATA_EXCL);
220
221         /* prepare the output status for the store */
222         inStatus.Mask = AFS_SETMODTIME;
223         inStatus.ClientModTime = scp->clientModTime;
224         scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
225
226         /* calculate truncation position */
227         truncPos = scp->length.LowPart;
228         if ((scp->mask & CM_SCACHEMASK_TRUNCPOS)
229                 && scp->truncPos.LowPart < (unsigned long) truncPos)
230                         truncPos = scp->truncPos.LowPart;
231         scp->mask &= ~CM_SCACHEMASK_TRUNCPOS;
232                 
233         lock_ReleaseMutex(&scp->mx);
234
235         cm_AFSFidFromFid(&tfid, &scp->fid);
236
237         /* now we're ready to do the store operation */
238         do {
239                 code = cm_Conn(&scp->fid, userp, reqp, &connp);
240                 if (code) continue;
241                 
242                 callp = rx_NewCall(connp->callp);
243
244                 code = StartRXAFS_StoreData(callp, &tfid, &inStatus,
245                         0, 0, truncPos);
246
247                 if (code == 0)
248                         code = EndRXAFS_StoreData(callp, &outStatus, &volSync);
249                 code = rx_EndCall(callp, code);
250                 
251         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, code));
252         code = cm_MapRPCError(code, reqp);
253         
254         /* now, clean up our state */
255         lock_ObtainMutex(&scp->mx);
256
257         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STOREDATA_EXCL);
258
259         if (code == 0) {
260                 /*
261                  * For explanation of handling of CM_SCACHEMASK_LENGTH,
262                  * see cm_BufWrite().
263                  */
264                 if (outStatus.Length >= scp->length.LowPart)
265                         scp->mask &= ~CM_SCACHEMASK_LENGTH;
266                 cm_MergeStatus(scp, &outStatus, &volSync, userp, 0);
267         }
268
269         return code;
270 }
271
272 long cm_BufRead(cm_buf_t *bufp, long nbytes, long *bytesReadp, cm_user_t *userp)
273 {
274         *bytesReadp = buf_bufferSize;
275
276         /* now return a code that means that I/O is done */
277         return 0;
278 }
279
280 /* stabilize scache entry, and return with it locked so 
281  * it stays stable.
282  */
283 long cm_BufStabilize(void *parmp, cm_user_t *userp, cm_req_t *reqp)
284 {
285         cm_scache_t *scp;
286         long code;
287
288         scp = parmp;
289         
290         lock_ObtainMutex(&scp->mx);
291         code = cm_SyncOp(scp, NULL, userp, reqp,
292                          0, CM_SCACHESYNC_NEEDCALLBACK
293                                 | CM_SCACHESYNC_GETSTATUS
294                                 | CM_SCACHESYNC_SETSIZE);
295         if (code) {
296                 lock_ReleaseMutex(&scp->mx);
297                 return code;
298         }
299         
300         return 0;
301 }
302
303 /* undoes the work that cm_BufStabilize does: releases lock so things can change again */
304 long cm_BufUnstabilize(void *parmp, cm_user_t *userp)
305 {
306         cm_scache_t *scp;
307         
308         scp = parmp;
309         
310         lock_ReleaseMutex(&scp->mx);
311         
312         /* always succeeds */
313         return 0;
314 }
315
316 cm_buf_ops_t cm_bufOps = {
317         cm_BufWrite,
318         cm_BufRead,
319         cm_BufStabilize,
320         cm_BufUnstabilize
321 };
322
323 int cm_InitDCache(long chunkSize, long nbuffers)
324 {
325         lock_InitializeMutex(&cm_bufGetMutex, "buf_Get mutex");
326         if (nbuffers) buf_nbuffers = nbuffers;
327         return buf_Init(&cm_bufOps);
328 }
329
330 /* check to see if we have an up-to-date buffer.  The buffer must have
331  * previously been obtained by calling buf_Get.
332  *
333  * Make sure we have a callback, and that the dataversion matches.
334  *
335  * Scp must be locked.
336  *
337  * Bufp *may* be locked.
338  */
339 int cm_HaveBuffer(cm_scache_t *scp, cm_buf_t *bufp, int isBufLocked)
340 {
341         int code;
342         if (!cm_HaveCallback(scp))
343                 return 0;
344         if ((bufp->cmFlags
345              & (CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED))
346                 == (CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED))
347                 return 1;
348         if (bufp->dataVersion == scp->dataVersion)
349                 return 1;
350         if (!isBufLocked) {
351                 code = lock_TryMutex(&bufp->mx);
352                 if (code == 0) {
353                         /* don't have the lock, and can't lock it, then
354                          * return failure.
355                          */
356                          return 0;
357                 }
358         }
359
360         /* remember dirty flag for later */
361         code = bufp->flags & CM_BUF_DIRTY;
362
363         /* release lock if we obtained it here */
364         if (!isBufLocked) lock_ReleaseMutex(&bufp->mx);
365
366         /* if buffer was dirty, buffer is acceptable for use */
367         if (code) return 1;
368         else return 0;
369 }
370
371 /* used when deciding whether to do a prefetch or not */
372 long cm_CheckFetchRange(cm_scache_t *scp, osi_hyper_t *startBasep, long length,
373         cm_user_t *up, cm_req_t *reqp, osi_hyper_t *realBasep)
374 {
375         osi_hyper_t toffset;
376         osi_hyper_t tbase;
377         long code;
378         cm_buf_t *bp;
379         int stop;
380         
381         /* now scan all buffers in the range, looking for any that look like
382          * they need work.
383          */
384         tbase = *startBasep;
385         stop = 0;
386         lock_ObtainMutex(&scp->mx);
387         while(length > 0) {
388                 /* get callback so we can do a meaningful dataVersion comparison */
389                 code = cm_SyncOp(scp, NULL, up, reqp, 0,
390                                  CM_SCACHESYNC_NEEDCALLBACK
391                                  | CM_SCACHESYNC_GETSTATUS);
392                 if (code) {
393                         scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
394                         lock_ReleaseMutex(&scp->mx);
395                         return code;
396                 }
397                 
398                 if (LargeIntegerGreaterThanOrEqualTo(tbase, scp->length)) {
399                         /* we're past the end of file */
400                         break;
401                 }
402
403                 bp = buf_Find(scp, &tbase);
404                 /* We cheat slightly by not locking the bp mutex. */
405                 if (bp) {
406                         if ((bp->cmFlags
407                               & (CM_BUF_CMFETCHING | CM_BUF_CMSTORING)) == 0
408                                 && bp->dataVersion != scp->dataVersion)
409                                         stop = 1;
410                         buf_Release(bp);
411                 }
412                 else stop = 1;
413
414                 /* if this buffer is essentially guaranteed to require a fetch,
415                  * break out here and return this position.
416                  */
417                 if (stop) break;
418                 
419                 toffset.LowPart = buf_bufferSize;
420                 toffset.HighPart = 0;
421                 tbase = LargeIntegerAdd(toffset, tbase);
422                 length -= buf_bufferSize;
423         }
424         
425         /* if we get here, either everything is fine or stop stopped us at a
426          * particular buffer in the range that definitely needs to be fetched.
427          */
428         if (stop == 0) {
429                 /* return non-zero code since realBasep won't be valid */
430                 scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
431                 code = -1;
432         }
433         else {
434                 /* successfully found a page that will need fetching */
435                 *realBasep = tbase;
436                 code = 0;
437         }
438         lock_ReleaseMutex(&scp->mx);
439         return code;
440 }
441
442 void cm_BkgStore(cm_scache_t *scp, long p1, long p2, long p3, long p4,
443         cm_user_t *userp)
444 {
445         osi_hyper_t toffset;
446         long length;
447         cm_req_t req;
448
449         cm_InitReq(&req);
450         req.flags |= CM_REQ_NORETRY;
451
452         toffset.LowPart = p1;
453         toffset.HighPart = p2;
454         length = p3;
455
456         osi_Log2(afsd_logp, "Starting BKG store vp 0x%x, base 0x%x", scp, p1);
457
458         cm_BufWrite(&scp->fid, &toffset, length, /* flags */ 0, userp, &req);
459
460         lock_ObtainMutex(&scp->mx);
461         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_ASYNCSTORE);
462         lock_ReleaseMutex(&scp->mx);
463 }
464
465 void cm_ClearPrefetchFlag(long code, cm_scache_t *scp, osi_hyper_t *base)
466 {
467         osi_hyper_t thyper;
468
469         if (code == 0) {
470                 thyper.LowPart = cm_chunkSize;
471                 thyper.HighPart = 0;
472                 thyper =  LargeIntegerAdd(*base, thyper);
473                 thyper.LowPart &= (-cm_chunkSize);
474                 if (LargeIntegerGreaterThan(*base, scp->prefetch.base))
475                         scp->prefetch.base = *base;
476                 if (LargeIntegerGreaterThan(thyper, scp->prefetch.end))
477                         scp->prefetch.end = thyper;
478         }
479         scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
480 }
481
482 /* do the prefetch */
483 void cm_BkgPrefetch(cm_scache_t *scp, long p1, long p2, long p3, long p4,
484         cm_user_t *userp)
485 {
486         long length;
487         osi_hyper_t base;
488         long code;
489         cm_buf_t *bp;
490         int cpff = 0;                   /* cleared prefetch flag */
491         cm_req_t req;
492
493         cm_InitReq(&req);
494         req.flags |= CM_REQ_NORETRY;
495         
496         base.LowPart = p1;
497         base.HighPart = p2;
498         length = p3;
499         
500         osi_Log2(afsd_logp, "Starting BKG prefetch vp 0x%x, base 0x%x", scp, p1);
501
502         code = buf_Get(scp, &base, &bp);
503
504         lock_ObtainMutex(&scp->mx);
505
506         if (code || (bp->cmFlags & CM_BUF_CMFETCHING)) {
507                 scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
508                 lock_ReleaseMutex(&scp->mx);
509                 return;
510         }
511
512         code = cm_GetBuffer(scp, bp, &cpff, userp, &req);
513         if (!cpff) cm_ClearPrefetchFlag(code, scp, &base);
514         lock_ReleaseMutex(&scp->mx);
515         buf_Release(bp);
516         return;
517 }
518
519 /* a read was issued to offsetp, and we have to determine whether we should
520  * do a prefetch.
521  */
522 void cm_ConsiderPrefetch(cm_scache_t *scp, osi_hyper_t *offsetp,
523         cm_user_t *userp, cm_req_t *reqp)
524 {
525         long code;
526         osi_hyper_t realBase;
527         osi_hyper_t readBase;
528         
529         readBase = *offsetp;
530         /* round up to chunk boundary */
531         readBase.LowPart += (cm_chunkSize-1);
532         readBase.LowPart &= (-cm_chunkSize);
533
534         lock_ObtainMutex(&scp->mx);
535         if ((scp->flags & CM_SCACHEFLAG_PREFETCHING)
536               || LargeIntegerLessThanOrEqualTo(readBase, scp->prefetch.base)) {
537                 lock_ReleaseMutex(&scp->mx);
538                 return;
539         }
540         scp->flags |= CM_SCACHEFLAG_PREFETCHING;
541
542         /* start the scan at the latter of the end of this read or
543          * the end of the last fetched region.
544          */
545         if (LargeIntegerGreaterThan(scp->prefetch.end, readBase))
546                 readBase = scp->prefetch.end;
547
548         lock_ReleaseMutex(&scp->mx);
549
550
551         code = cm_CheckFetchRange(scp, &readBase, cm_chunkSize, userp, reqp,
552                 &realBase);
553         if (code) return;       /* can't find something to prefetch */
554
555         osi_Log2(afsd_logp, "BKG Prefetch request vp 0x%x, base 0x%x",
556                  scp, realBase.LowPart);
557
558         cm_QueueBKGRequest(scp, cm_BkgPrefetch, realBase.LowPart,
559                 realBase.HighPart, cm_chunkSize, 0, userp);
560 }
561
562 /* scp must be locked; temporarily unlocked during processing.
563  * If returns 0, returns buffers held in biop, and with
564  * CM_BUF_CMSTORING set.
565  *
566  * Caller *must* set CM_BUF_WRITING and reset the over.hEvent field if the
567  * buffer is ever unlocked before CM_BUF_DIRTY is cleared.  And if
568  * CM_BUF_WRITING is ever viewed by anyone, then it must be cleared, sleepers
569  * must be woken, and the event must be set when the I/O is done.  All of this
570  * is required so that buf_WaitIO synchronizes properly with the buffer as it
571  * is being written out.
572  */
573 long cm_SetupStoreBIOD(cm_scache_t *scp, osi_hyper_t *inOffsetp, long inSize,
574         cm_bulkIO_t *biop, cm_user_t *userp, cm_req_t *reqp)
575 {
576         cm_buf_t *bufp;
577         osi_queueData_t *qdp;
578         osi_hyper_t thyper;
579         osi_hyper_t tbase;
580         osi_hyper_t scanStart;          /* where to start scan for dirty pages */
581         osi_hyper_t scanEnd;            /* where to stop scan for dirty pages */
582         osi_hyper_t firstModOffset;     /* offset of first modified page in range */
583         long temp;
584         long code;
585         long flags;                     /* flags to cm_SyncOp */
586         
587         /* clear things out */
588         biop->scp = scp;                /* don't hold */
589         biop->offset = *inOffsetp;
590         biop->length = 0;
591         biop->bufListp = NULL;
592         biop->bufListEndp = NULL;
593         biop->reserved = 0;
594
595         /* reserve a chunk's worth of buffers */
596         lock_ReleaseMutex(&scp->mx);
597         buf_ReserveBuffers(cm_chunkSize / buf_bufferSize);
598         lock_ObtainMutex(&scp->mx);
599
600         bufp = NULL;
601         for(temp = 0; temp < inSize; temp += buf_bufferSize, bufp = NULL) {
602                 thyper.HighPart = 0;
603                 thyper.LowPart = temp;
604                 tbase = LargeIntegerAdd(*inOffsetp, thyper);
605                 
606                 bufp = buf_Find(scp, &tbase);
607                 if (bufp) {
608                         /* get buffer mutex and scp mutex safely */
609                         lock_ReleaseMutex(&scp->mx);
610                         lock_ObtainMutex(&bufp->mx);
611                         lock_ObtainMutex(&scp->mx);
612
613                         flags = CM_SCACHESYNC_NEEDCALLBACK
614                                 | CM_SCACHESYNC_GETSTATUS
615                                 | CM_SCACHESYNC_STOREDATA
616                                 | CM_SCACHESYNC_BUFLOCKED;
617                         code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags); 
618                         if (code) {
619                                 lock_ReleaseMutex(&bufp->mx);
620                                 buf_Release(bufp);
621                                 buf_UnreserveBuffers(cm_chunkSize
622                                                         / buf_bufferSize);
623                                 return code;
624                         }
625                         
626                         /* if the buffer is dirty, we're done */
627                         if (bufp->flags & CM_BUF_DIRTY) {
628                                 osi_assertx(!(bufp->flags & CM_BUF_WRITING),
629                                         "WRITING w/o CMSTORING in SetupStoreBIOD");
630                                 bufp->flags |= CM_BUF_WRITING;
631                                 break;
632                         }
633
634                         /* this buffer is clean, so there's no reason to process it */
635                         cm_SyncOpDone(scp, bufp, flags);
636                         lock_ReleaseMutex(&bufp->mx);
637                         buf_Release(bufp);
638                 }
639         }
640
641         biop->reserved = 1;
642         
643         /* if we get here, if bufp is null, we didn't find any dirty buffers
644          * that weren't already being stored back, so we just quit now.
645          */
646         if (!bufp) {
647                 return 0;
648         }
649
650         /* don't need buffer mutex any more */
651         lock_ReleaseMutex(&bufp->mx);
652         
653         /* put this element in the list */
654         qdp = osi_QDAlloc();
655         osi_SetQData(qdp, bufp);
656         /* don't have to hold bufp, since held by buf_Find above */
657         osi_QAddH((osi_queue_t **) &biop->bufListp,
658                   (osi_queue_t **) &biop->bufListEndp,
659                   &qdp->q);
660         biop->length = buf_bufferSize;
661         firstModOffset = bufp->offset;
662         biop->offset = firstModOffset;
663
664         /* compute the window surrounding *inOffsetp of size cm_chunkSize */
665         scanStart = *inOffsetp;
666         scanStart.LowPart &= (-cm_chunkSize);
667         thyper.LowPart = cm_chunkSize;
668         thyper.HighPart = 0;
669         scanEnd = LargeIntegerAdd(scanStart, thyper);
670
671         flags = CM_SCACHESYNC_NEEDCALLBACK
672                 | CM_SCACHESYNC_GETSTATUS
673                 | CM_SCACHESYNC_STOREDATA
674                 | CM_SCACHESYNC_BUFLOCKED
675                 | CM_SCACHESYNC_NOWAIT;
676
677         /* start by looking backwards until scanStart */
678         thyper.HighPart = 0;            /* hyper version of buf_bufferSize */
679         thyper.LowPart = buf_bufferSize;
680         tbase = LargeIntegerSubtract(firstModOffset, thyper);
681         while(LargeIntegerGreaterThanOrEqualTo(tbase, scanStart)) {
682                 /* see if we can find the buffer */
683                 bufp = buf_Find(scp, &tbase);
684                 if (!bufp) break;
685
686                 /* try to lock it, and quit if we can't (simplifies locking) */
687                 code = lock_TryMutex(&bufp->mx);
688                 if (code == 0) {
689                         buf_Release(bufp);
690                         break;
691                 }
692                 
693                 code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags);
694                 if (code) {
695                         lock_ReleaseMutex(&bufp->mx);
696                         buf_Release(bufp);
697                         break;
698                 }
699                 
700                 if (!(bufp->flags & CM_BUF_DIRTY)) {
701                         /* buffer is clean, so we shouldn't add it */
702                         cm_SyncOpDone(scp, bufp, flags);
703                         lock_ReleaseMutex(&bufp->mx);
704                         buf_Release(bufp);
705                         break;
706                 }
707
708                 /* don't need buffer mutex any more */
709                 lock_ReleaseMutex(&bufp->mx);
710
711                 /* we have a dirty buffer ready for storing.  Add it to the tail
712                  * of the list, since it immediately precedes all of the disk
713                  * addresses we've already collected.
714                  */
715                 qdp = osi_QDAlloc();
716                 osi_SetQData(qdp, bufp);
717                 /* no buf_hold necessary, since we have it held from buf_Find */
718                 osi_QAddT((osi_queue_t **) &biop->bufListp,
719                           (osi_queue_t **) &biop->bufListEndp,
720                           &qdp->q);
721
722                 /* update biod info describing the transfer */
723                 biop->offset = LargeIntegerSubtract(biop->offset, thyper);
724                 biop->length += buf_bufferSize;
725                 
726                 /* update loop pointer */
727                 tbase = LargeIntegerSubtract(tbase, thyper);
728         }       /* while loop looking for pages preceding the one we found */
729
730         /* now, find later dirty, contiguous pages, and add them to the list */
731         thyper.HighPart = 0;            /* hyper version of buf_bufferSize */
732         thyper.LowPart = buf_bufferSize;
733         tbase = LargeIntegerAdd(firstModOffset, thyper);
734         while(LargeIntegerLessThan(tbase, scanEnd)) {
735                 /* see if we can find the buffer */
736                 bufp = buf_Find(scp, &tbase);
737                 if (!bufp) break;
738
739                 /* try to lock it, and quit if we can't (simplifies locking) */
740                 code = lock_TryMutex(&bufp->mx);
741                 if (code == 0) {
742                         buf_Release(bufp);
743                         break;
744                 }
745                 
746                 code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags);
747                 if (code) {
748                         lock_ReleaseMutex(&bufp->mx);
749                         buf_Release(bufp);
750                         break;
751                 }
752                 
753                 if (!(bufp->flags & CM_BUF_DIRTY)) {
754                         /* buffer is clean, so we shouldn't add it */
755                         cm_SyncOpDone(scp, bufp, flags);
756                         lock_ReleaseMutex(&bufp->mx);
757                         buf_Release(bufp);
758                         break;
759                 }
760
761                 /* don't need buffer mutex any more */
762                 lock_ReleaseMutex(&bufp->mx);
763
764                 /* we have a dirty buffer ready for storing.  Add it to the head
765                  * of the list, since it immediately follows all of the disk
766                  * addresses we've already collected.
767                  */
768                 qdp = osi_QDAlloc();
769                 osi_SetQData(qdp, bufp);
770                 /* no buf_hold necessary, since we have it held from buf_Find */
771                 osi_QAddH((osi_queue_t **) &biop->bufListp,
772                           (osi_queue_t **) &biop->bufListEndp,
773                           &qdp->q);
774
775                 /* update biod info describing the transfer */
776                 biop->length += buf_bufferSize;
777                 
778                 /* update loop pointer */
779                 tbase = LargeIntegerAdd(tbase, thyper);
780         }       /* while loop looking for pages following the first page we found */
781         
782         /* finally, we're done */
783         return 0;
784 }
785
786 /* scp must be locked; temporarily unlocked during processing.
787  * If returns 0, returns buffers held in biop, and with
788  * CM_BUF_CMFETCHING flags set.
789  * If an error is returned, we don't return any buffers.
790  */
791 long cm_SetupFetchBIOD(cm_scache_t *scp, osi_hyper_t *offsetp,
792                         cm_bulkIO_t *biop, cm_user_t *up, cm_req_t *reqp)
793 {
794         long code;
795         cm_buf_t *tbp;
796         osi_hyper_t toffset;            /* a long long temp variable */
797         osi_hyper_t pageBase;           /* base offset we're looking at */
798         osi_queueData_t *qdp;           /* one temp queue structure */
799         osi_queueData_t *tqdp;          /* another temp queue structure */
800         long collected;                 /* how many bytes have been collected */
801         int isFirst;
802         long flags;
803         osi_hyper_t fileSize;           /* the # of bytes in the file */
804         osi_queueData_t *heldBufListp;  /* we hold all buffers in this list */
805         osi_queueData_t *heldBufListEndp;       /* first one */
806         int reserving;
807         
808         biop->scp = scp;
809         biop->offset = *offsetp;
810         /* null out the list of buffers */
811         biop->bufListp = biop->bufListEndp = NULL;
812         biop->reserved = 0;
813
814         /* first lookup the file's length, so we know when to stop */
815         code = cm_SyncOp(scp, NULL, up, reqp, 0, CM_SCACHESYNC_NEEDCALLBACK
816                                                  | CM_SCACHESYNC_GETSTATUS);
817         if (code) return code;
818         
819         /* copy out size, since it may change */
820         fileSize = scp->serverLength;
821         
822         lock_ReleaseMutex(&scp->mx);
823
824         pageBase = *offsetp;
825         collected = pageBase.LowPart & (cm_chunkSize - 1);
826         heldBufListp = NULL;
827         heldBufListEndp = NULL;
828
829         /*
830          * Obtaining buffers can cause dirty buffers to be recycled, which
831          * can cause a storeback, so cannot be done while we have buffers
832          * reserved.
833          *
834          * To get around this, we get buffers twice.  Before reserving buffers,
835          * we obtain and release each one individually.  After reserving
836          * buffers, we try to obtain them again, but only by lookup, not by
837          * recycling.  If a buffer has gone away while we were waiting for
838          * the others, we just use whatever buffers we already have.
839          *
840          * On entry to this function, we are already holding a buffer, so we
841          * can't wait for reservation.  So we call buf_TryReserveBuffers()
842          * instead.  Not only that, we can't really even call buf_Get(), for
843          * the same reason.  We can't avoid that, though.  To avoid deadlock
844          * we allow only one thread to be executing the buf_Get()-buf_Release()
845          * sequence at a time.
846          */
847
848         /* first hold all buffers, since we can't hold any locks in buf_Get */
849         while (1) {
850                 /* stop at chunk boundary */
851                 if (collected >= cm_chunkSize) break;
852                 
853                 /* see if the next page would be past EOF */
854                 if (LargeIntegerGreaterThanOrEqualTo(pageBase, fileSize)) break;
855
856                 lock_ObtainMutex(&cm_bufGetMutex);
857
858                 code = buf_Get(scp, &pageBase, &tbp);
859                 if (code) {
860                         lock_ReleaseMutex(&cm_bufGetMutex);
861                         lock_ObtainMutex(&scp->mx);
862                         return code;
863                 }
864                 
865                 buf_Release(tbp);
866
867                 lock_ReleaseMutex(&cm_bufGetMutex);
868
869                 toffset.HighPart = 0;
870                 toffset.LowPart = buf_bufferSize;
871                 pageBase = LargeIntegerAdd(toffset, pageBase);
872                 collected += buf_bufferSize;
873         }
874
875         /* reserve a chunk's worth of buffers if possible */
876         reserving = buf_TryReserveBuffers(cm_chunkSize / buf_bufferSize);
877
878         pageBase = *offsetp;
879         collected = pageBase.LowPart & (cm_chunkSize - 1);
880
881         /* now hold all buffers, if they are still there */
882         while (1) {
883                 /* stop at chunk boundary */
884                 if (collected >= cm_chunkSize) break;
885                 
886                 /* see if the next page would be past EOF */
887                 if (LargeIntegerGreaterThanOrEqualTo(pageBase, fileSize)) break;
888
889                 tbp = buf_Find(scp, &pageBase);
890                 if (!tbp) break;
891
892                 /* add the buffer to the list */
893                 qdp = osi_QDAlloc();
894                 osi_SetQData(qdp, tbp);
895                 osi_QAdd((osi_queue_t **)&heldBufListp, &qdp->q);
896                 if (!heldBufListEndp) heldBufListEndp = qdp;
897                 /* leave tbp held (from buf_Get) */
898
899                 if (!reserving) break;
900
901                 collected += buf_bufferSize;
902                 toffset.HighPart = 0;
903                 toffset.LowPart = buf_bufferSize;
904                 pageBase = LargeIntegerAdd(toffset, pageBase);
905         }
906
907         /* look at each buffer, adding it into the list if it looks idle and
908          * filled with old data.  One special case: wait for idle if it is the
909          * first buffer since we really need that one for our caller to make
910          * any progress.
911          */
912         isFirst = 1;
913         collected = 0;          /* now count how many we'll really use */
914         for(tqdp = heldBufListEndp;
915             tqdp;
916             tqdp = (osi_queueData_t *) osi_QPrev(&tqdp->q)) {
917                 /* get a ptr to the held buffer */
918                 tbp = osi_GetQData(tqdp);
919                 pageBase = tbp->offset;
920
921                 /* now lock the buffer lock */
922                 lock_ObtainMutex(&tbp->mx);
923                 lock_ObtainMutex(&scp->mx);
924
925                 /* don't bother fetching over data that is already current */
926                 if (tbp->dataVersion == scp->dataVersion) {
927                         /* we don't need this buffer, since it is current */
928                         lock_ReleaseMutex(&scp->mx);
929                         lock_ReleaseMutex(&tbp->mx);
930                         break;
931                 }
932
933                 flags = CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_FETCHDATA
934                         | CM_SCACHESYNC_BUFLOCKED;
935                 if (!isFirst) flags |= CM_SCACHESYNC_NOWAIT;
936
937                 /* wait for the buffer to serialize, if required.  Doesn't
938                  * release the scp or buffer lock(s) if NOWAIT is specified.
939                  */
940                 code = cm_SyncOp(scp, tbp, up, reqp, 0, flags);
941                 if (code) {
942                         lock_ReleaseMutex(&scp->mx);
943                         lock_ReleaseMutex(&tbp->mx);
944                         break;
945                 }
946                 
947                 /* don't fetch over dirty buffers */
948                 if (tbp->flags & CM_BUF_DIRTY) {
949                         cm_SyncOpDone(scp, tbp, flags);
950                         lock_ReleaseMutex(&scp->mx);
951                         lock_ReleaseMutex(&tbp->mx);
952                         break;
953                 }
954
955                 /* Release locks */
956                 lock_ReleaseMutex(&scp->mx);
957                 lock_ReleaseMutex(&tbp->mx);
958
959                 /* add the buffer to the list */
960                 qdp = osi_QDAlloc();
961                 osi_SetQData(qdp, tbp);
962                 osi_QAdd((osi_queue_t **)&biop->bufListp, &qdp->q);
963                 if (!biop->bufListEndp) biop->bufListEndp = qdp;
964                 buf_Hold(tbp);
965
966                 /* from now on, a failure just stops our collection process, but
967                  * we still do the I/O to whatever we've already managed to collect.
968                  */
969                 isFirst = 0;
970                 collected += buf_bufferSize;
971         }
972         
973         /* now, we've held in biop->bufListp all the buffer's we're really
974          * interested in.  We also have holds left from heldBufListp, and we
975          * now release those holds on the buffers.
976          */
977         for(qdp = heldBufListp; qdp; qdp = tqdp) {
978                 tqdp = (osi_queueData_t *) osi_QNext(&qdp->q);
979                 tbp = osi_GetQData(qdp);
980                 osi_QDFree(qdp);
981                 buf_Release(tbp);
982         }
983
984         /* Caller expects this */
985         lock_ObtainMutex(&scp->mx);
986  
987         /* if we got a failure setting up the first buffer, then we don't have
988          * any side effects yet, and we also have failed an operation that the
989          * caller requires to make any progress.  Give up now.
990          */
991         if (code && isFirst) {
992                 buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
993                 return code;
994         }
995         
996         /* otherwise, we're still OK, and should just return the I/O setup we've
997          * got.
998          */
999         biop->length = collected;
1000         biop->reserved = reserving;
1001         return 0;
1002 }
1003
1004 /* release a bulk I/O structure that was setup by cm_SetupFetchBIOD or by
1005  * cm_SetupStoreBIOD
1006  */
1007 void cm_ReleaseBIOD(cm_bulkIO_t *biop, int isStore)
1008 {
1009         cm_scache_t *scp;
1010         cm_buf_t *bufp;
1011         osi_queueData_t *qdp;
1012         osi_queueData_t *nqdp;
1013         int flags;
1014
1015         /* Give back reserved buffers */
1016         if (biop->reserved)
1017                 buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
1018         
1019         flags = CM_SCACHESYNC_NEEDCALLBACK;
1020         if (isStore)
1021                 flags |= CM_SCACHESYNC_STOREDATA;
1022         else
1023                 flags |= CM_SCACHESYNC_FETCHDATA;
1024
1025         scp = biop->scp;
1026         for(qdp = biop->bufListp; qdp; qdp = nqdp) {
1027                 /* lookup next guy first, since we're going to free this one */
1028                 nqdp = (osi_queueData_t *) osi_QNext(&qdp->q);
1029                 
1030                 /* extract buffer and free queue data */
1031                 bufp = osi_GetQData(qdp);
1032                 osi_QDFree(qdp);
1033                 
1034                 /* now, mark I/O as done, unlock the buffer and release it */
1035                 lock_ObtainMutex(&bufp->mx);
1036                 lock_ObtainMutex(&scp->mx);
1037                 cm_SyncOpDone(scp, bufp, flags);
1038                 lock_ReleaseMutex(&scp->mx);
1039                 
1040                 /* turn off writing and wakeup users */
1041                 if (isStore) {
1042                         if (bufp->flags & CM_BUF_WAITING) {
1043                                 osi_Wakeup((long) bufp);
1044                         }
1045                         bufp->flags &= ~(CM_BUF_WAITING | CM_BUF_WRITING
1046                                           | CM_BUF_DIRTY);
1047                 }
1048
1049                 lock_ReleaseMutex(&bufp->mx);
1050                 buf_Release(bufp);
1051         }
1052         
1053         /* clean things out */
1054         biop->bufListp = NULL;
1055         biop->bufListEndp = NULL;
1056 }
1057
1058 /* Fetch a buffer.  Called with scp locked.
1059  * The scp is locked on return.
1060  */
1061 long cm_GetBuffer(cm_scache_t *scp, cm_buf_t *bufp, int *cpffp, cm_user_t *up,
1062         cm_req_t *reqp)
1063 {
1064         long code;
1065         long nbytes;                    /* bytes in transfer */
1066         long rbytes;                    /* bytes in rx_Read call */
1067         long temp;
1068         AFSFetchStatus afsStatus;
1069         AFSCallBack callback;
1070         AFSVolSync volSync;
1071         char *bufferp;
1072         cm_buf_t *tbufp;                /* buf we're filling */
1073         osi_queueData_t *qdp;           /* q element we're scanning */
1074         AFSFid tfid;
1075         struct rx_call *callp;
1076         cm_bulkIO_t biod;               /* bulk IO descriptor */
1077         cm_conn_t *connp;
1078
1079         /* now, the buffer may or may not be filled with good data (buf_GetNew
1080          * drops lots of locks, and may indeed return a properly initialized
1081          * buffer, although more likely it will just return a new, empty, buffer.
1082          */
1083         cm_AFSFidFromFid(&tfid, &scp->fid);
1084
1085         code = cm_SetupFetchBIOD(scp, &bufp->offset, &biod, up, reqp);
1086         if (code) {
1087                 /* couldn't even get the first page setup properly */
1088                 osi_Log1(afsd_logp, "SetupFetchBIOD failure code %d", code);
1089                 return code;
1090         }
1091
1092         /* once we get here, we have the callback in place, we know that no one
1093          * is fetching the data now.  Check one last time that we still have
1094          * the wrong data, and then fetch it if we're still wrong.
1095          *
1096          * We can lose a race condition and end up with biod.length zero, in
1097          * which case we just retry.
1098          */
1099         if (bufp->dataVersion == scp->dataVersion || biod.length == 0) {
1100                 osi_Log3(afsd_logp, "Bad DVs %d, %d or length 0x%x",
1101                          bufp->dataVersion, scp->dataVersion, biod.length);
1102                 if ((bufp->dataVersion == -1
1103                      || bufp->dataVersion < scp->dataVersion)
1104                     && LargeIntegerGreaterThanOrEqualTo(bufp->offset,
1105                                                         scp->serverLength)) {
1106                         if (bufp->dataVersion == -1)
1107                                 memset(bufp->datap, 0, buf_bufferSize);
1108                         bufp->dataVersion = scp->dataVersion;
1109                 }
1110                 lock_ReleaseMutex(&scp->mx);
1111                 cm_ReleaseBIOD(&biod, 0);
1112                 lock_ObtainMutex(&scp->mx);
1113                 return 0;
1114         }
1115         
1116         lock_ReleaseMutex(&scp->mx);
1117
1118 #ifdef DISKCACHE95
1119         DPRINTF("cm_GetBuffer: fetching data scpDV=%d bufDV=%d scp=%x bp=%x dcp=%x\n",
1120                 scp->dataVersion, bufp->dataVersion, scp, bufp, bufp->dcp);
1121 #endif /* DISKCACHE95 */
1122
1123         /* now make the call */
1124         do {
1125                 code = cm_Conn(&scp->fid, up, reqp, &connp);
1126                 if (code) continue;
1127                 
1128                 callp = rx_NewCall(connp->callp);
1129
1130                 osi_Log3(afsd_logp, "CALL FetchData vp %x, off 0x%x, size 0x%x",
1131                         (long) scp, biod.offset.LowPart, biod.length);
1132
1133                 code = StartRXAFS_FetchData(callp, &tfid, biod.offset.LowPart,
1134                         biod.length);
1135
1136                 /* now copy the data out of the pipe and put it in the buffer */
1137                 temp  = rx_Read(callp, &nbytes, 4);
1138                 if (temp == 4) {
1139                         nbytes = ntohl(nbytes);
1140                         if (nbytes > biod.length) code = -1;
1141                 }
1142                 else code = -1;
1143
1144                 if (code == 0) {
1145                         qdp = biod.bufListEndp;
1146                         if (qdp) {
1147                                 tbufp = osi_GetQData(qdp);
1148                                 bufferp = tbufp->datap;
1149                         }
1150                         else bufferp = NULL;
1151                         /* fill nbytes of data from the pipe into the pages.
1152                          * When we stop, qdp will point at the last page we're
1153                          * dealing with, and bufferp will tell us where we
1154                          * stopped.  We'll need this info below when we clear
1155                          * the remainder of the last page out (and potentially
1156                          * clear later pages out, if we fetch past EOF).
1157                          */
1158                         while(nbytes > 0) {
1159                                 /* assert that there are still more buffers;
1160                                  * our check above for nbytes being less than
1161                                  * biod.length should ensure this.
1162                                  */
1163                                 osi_assert(bufferp != NULL);
1164
1165                                 /* read rbytes of data */
1166                                 rbytes = (nbytes > buf_bufferSize? buf_bufferSize : nbytes);
1167                                 temp = rx_Read(callp, bufferp, rbytes);
1168                                 if (temp < rbytes) {
1169                                         code = -1;
1170                                         break;
1171                                 }
1172
1173                                 /* allow read-while-fetching.
1174                                  * if this is the last buffer, clear the
1175                                  * PREFETCHING flag, so the reader waiting for
1176                                  * this buffer will start a prefetch.
1177                                  */
1178                                 tbufp->cmFlags |= CM_BUF_CMFULLYFETCHED;
1179                                 lock_ObtainMutex(&scp->mx);
1180                                 if (scp->flags & CM_SCACHEFLAG_WAITING) {
1181                                         scp->flags &= ~CM_SCACHEFLAG_WAITING;
1182                                         osi_Wakeup((long) &scp->flags);
1183                                 }
1184                                 if (cpffp && !*cpffp && !osi_QPrev(&qdp->q)) {
1185                                         *cpffp = 1;
1186                                         cm_ClearPrefetchFlag(0, scp,
1187                                                              &biod.offset);
1188                                 }
1189                                 lock_ReleaseMutex(&scp->mx);
1190
1191                                 /* and adjust counters */
1192                                 nbytes -= temp;
1193                                 
1194                                 /* and move to the next buffer */
1195                                 if (nbytes != 0) {
1196                                         qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
1197                                         if (qdp) {
1198                                                 tbufp = osi_GetQData(qdp);
1199                                                 bufferp = tbufp->datap;
1200                                         }
1201                                         else bufferp = NULL;
1202                                 } else bufferp += temp;
1203                         }
1204
1205                         /* zero out remainder of last pages, in case we are
1206                          * fetching past EOF.  We were fetching an integral #
1207                          * of pages, but stopped, potentially in the middle of
1208                          * a page.  Zero the remainder of that page, and then
1209                          * all of the rest of the pages.
1210                          */
1211                         /* bytes fetched */
1212                         rbytes = bufferp - tbufp->datap;
1213                         /* bytes left to zero */
1214                         rbytes = buf_bufferSize - rbytes;
1215                         while(qdp) {
1216                                 if (rbytes != 0)
1217                                         memset(bufferp, 0, rbytes);
1218                                 qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
1219                                 if (qdp == NULL) break;
1220                                 tbufp = osi_GetQData(qdp);
1221                                 bufferp = tbufp->datap;
1222                                 /* bytes to clear in this page */
1223                                 rbytes = buf_bufferSize;
1224                         }
1225                 }
1226
1227                 if (code == 0)
1228                         code = EndRXAFS_FetchData(callp, &afsStatus, &callback, &volSync);
1229                 code = rx_EndCall(callp, code);
1230                 osi_Log0(afsd_logp, "CALL FetchData DONE");
1231                 
1232         } while (cm_Analyze(connp, up, reqp, &scp->fid, &volSync, NULL, code));
1233         code = cm_MapRPCError(code, reqp);
1234
1235         lock_ObtainMutex(&scp->mx);
1236         /* we know that no one else has changed the buffer, since we still have
1237          * the fetching flag on the buffers, and we have the scp locked again.
1238          * Copy in the version # into the buffer if we got code 0 back from the
1239          * read.
1240          */
1241         if (code == 0) {
1242                 for(qdp = biod.bufListp;
1243                     qdp;
1244                     qdp = (osi_queueData_t *) osi_QNext(&qdp->q)) {
1245                         tbufp = osi_GetQData(qdp);
1246                         tbufp->dataVersion = afsStatus.DataVersion;
1247
1248 #ifdef DISKCACHE95
1249                         /* write buffer out to disk cache */
1250                         diskcache_Update(tbufp->dcp, tbufp->datap, buf_bufferSize,
1251                                          tbufp->dataVersion);
1252 #endif /* DISKCACHE95 */
1253                 }
1254         }
1255
1256         /* release scatter/gather I/O structure (buffers, locks) */
1257         lock_ReleaseMutex(&scp->mx);
1258         cm_ReleaseBIOD(&biod, 0);
1259         lock_ObtainMutex(&scp->mx);
1260
1261         if (code == 0) cm_MergeStatus(scp, &afsStatus, &volSync, up, 0);
1262         return code;
1263 }