05584e8571a2fc34fd5e626931eafcf09e0225aa
[openafs.git] / src / afs / afs_segments.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  * --------------------- Required definitions ---------------------
12  */
13 #include <afsconfig.h>
14 #include "../afs/param.h"
15
16 RCSID("$Header$");
17
18 #include "../afs/sysincludes.h" /*Standard vendor system headers*/
19 #include "../afs/afsincludes.h" /*AFS-based standard headers*/
20 #include "../afs/afs_stats.h"  /* statistics */
21 #include "../afs/afs_cbqueue.h"
22 #include "../afs/afs_osidnlc.h"
23
24
25
26 /* Imported variables */
27 extern afs_rwlock_t afs_xserver;
28 extern afs_rwlock_t afs_xdcache;
29 extern afs_rwlock_t afs_xcbhash;
30 extern afs_lock_t afs_ftf;
31 extern struct server *afs_servers[NSERVERS];
32 extern afs_int32 afs_dhashsize;
33 extern afs_int32 *afs_dvhashTbl;
34 extern unsigned char *afs_indexFlags;   /*(only one) Is there data there?*/
35 extern int cacheDiskType;
36
37 afs_uint32 afs_stampValue=0;
38
39 /*
40  * afs_StoreMini
41  *
42  * Description:
43  *      Send a truncation request to a FileServer.
44  *
45  * Parameters:
46  *      xxx : description
47  *
48  * Environment:
49  *      We're write-locked upon entry.
50  */
51
52 int afs_StoreMini(avc, areq)
53     register struct vcache *avc;
54     struct vrequest *areq;
55
56 { /*afs_StoreMini*/
57     register struct conn *tc;
58     struct AFSStoreStatus InStatus;
59     struct AFSFetchStatus OutStatus;
60     struct AFSVolSync tsync;
61     register afs_int32 code;
62     register struct rx_call *tcall;
63     afs_int32 tlen;
64     XSTATS_DECLS
65
66     AFS_STATCNT(afs_StoreMini);
67     afs_Trace2(afs_iclSetp, CM_TRACE_STOREMINI, ICL_TYPE_POINTER, avc,
68                ICL_TYPE_INT32, avc->m.Length);
69     tlen = avc->m.Length;
70     if (avc->truncPos < tlen) tlen = avc->truncPos;
71     avc->truncPos = AFS_NOTRUNC;
72     avc->states &= ~CExtendedFile;
73
74     do {
75         tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
76         if (tc) {
77 #ifdef RX_ENABLE_LOCKS
78             AFS_GUNLOCK();
79 #endif /* RX_ENABLE_LOCKS */
80             tcall = rx_NewCall(tc->id);
81 #ifdef RX_ENABLE_LOCKS
82             AFS_GLOCK();
83 #endif /* RX_ENABLE_LOCKS */
84             /* Set the client mod time since we always want the file
85              * to have the client's mod time and not the server's one
86              * (to avoid problems with make, etc.) It almost always
87              * works fine with standard afs because them server/client
88              * times are in sync and more importantly this storemini
89              * it's a special call that would typically be followed by
90              * the proper store-data or store-status calls.
91              */
92             InStatus.Mask = AFS_SETMODTIME;
93             InStatus.ClientModTime = avc->m.Date;
94             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STOREDATA);
95 #ifdef RX_ENABLE_LOCKS
96             AFS_GUNLOCK();
97 #endif /* RX_ENABLE_LOCKS */
98             code = StartRXAFS_StoreData(tcall,
99                                         (struct AFSFid *)&avc->fid.Fid,
100                                         &InStatus, avc->m.Length, 0, tlen);
101             if (code == 0) {
102                 code = EndRXAFS_StoreData(tcall, &OutStatus, &tsync);
103             }
104             code = rx_EndCall(tcall, code);
105 #ifdef RX_ENABLE_LOCKS
106             AFS_GLOCK();
107 #endif /* RX_ENABLE_LOCKS */
108             XSTATS_END_TIME;
109         }
110         else code = -1;
111     } while
112         (afs_Analyze(tc, code, &avc->fid, areq,
113                      AFS_STATS_FS_RPCIDX_STOREDATA,
114                      SHARED_LOCK, (struct cell *)0));
115
116     if (code == 0) {
117         afs_ProcessFS(avc, &OutStatus, areq);
118     }
119     else {
120         /* blew it away */
121         afs_InvalidateAllSegments(avc, 1);
122     }
123     return code;
124
125 } /*afs_StoreMini*/
126
127 unsigned int storeallmissing = 0;
128 #define lmin(a,b) (((a) < (b)) ? (a) : (b))
129 /*
130  * afs_StoreAllSegments
131  *
132  * Description:
133  *      Stores all modified segments back to server
134  *
135  * Parameters:
136  *      avc  : Pointer to vcache entry.
137  *      areq : Pointer to request structure.
138  *
139  * Environment:
140  *      Called with avc write-locked.
141  */
142 #if defined (AFS_HPUX_ENV) || defined(AFS_ULTRIX_ENV)
143 int NCHUNKSATONCE = 3;
144 #else
145 int NCHUNKSATONCE = 64 ;
146 #endif
147 int afs_dvhack=0;
148
149
150 afs_StoreAllSegments(avc, areq, sync)
151     register struct vcache *avc;
152     struct vrequest *areq;
153     int sync;
154
155 { /*afs_StoreAllSegments*/
156     register struct dcache *tdc;
157     register afs_int32 code=0;
158     register afs_int32 index;
159     register afs_int32 origCBs, foreign=0;
160     int hash, stored;
161     afs_hyper_t newDV, oldDV;   /* DV when we start, and finish, respectively */
162     struct dcache **dcList, **dclist;
163     unsigned int i, j, minj, maxj, moredata, high, off;
164     unsigned long tlen;
165     int safety;
166     int maxStoredLength; /* highest offset we've written to server. */
167 #ifndef AFS_NOSTATS
168     struct afs_stats_xferData *xferP;   /* Ptr to this op's xfer struct */
169     osi_timeval_t  xferStartTime,       /*FS xfer start time*/
170                    xferStopTime;        /*FS xfer stop time*/
171     afs_int32 bytesToXfer;                      /* # bytes to xfer*/
172     afs_int32 bytesXferred;                     /* # bytes actually xferred*/
173 #endif /* AFS_NOSTATS */
174
175
176     AFS_STATCNT(afs_StoreAllSegments);
177  
178     hset(oldDV, avc->m.DataVersion);
179     hset(newDV, avc->m.DataVersion);
180     hash = DVHash(&avc->fid);
181     foreign = (avc->states & CForeign);
182     dcList = (struct dcache **) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
183     afs_Trace2(afs_iclSetp, CM_TRACE_STOREALL, ICL_TYPE_POINTER, avc,
184                ICL_TYPE_INT32, avc->m.Length);
185 #ifndef AFS_AIX32_ENV
186     /* In the aix vm implementation we need to do the vm_writep even
187      * on the memcache case since that's we adjust the file's size
188      * and finish flushing partial vm pages.
189      */
190     if (cacheDiskType != AFS_FCACHE_TYPE_MEM) 
191 #endif /* AFS_AIX32_ENV */
192     {
193         /* If we're not diskless, reading a file may stress the VM
194          * system enough to cause a pageout, and this vnode would be
195          * locked when the pageout occurs.  We can prevent this problem
196          * by making sure all dirty pages are already flushed.  We don't
197          * do this when diskless because reading a diskless (i.e.
198          * memory-resident) chunk doesn't require using new VM, and we
199          * also don't want to dump more dirty data into a diskless cache,
200          * since they're smaller, and we might exceed its available
201          * space.
202          */
203 #if     defined(AFS_SUN5_ENV)
204         if ( sync & AFS_VMSYNC_INVAL) /* invalidate VM pages */
205             osi_VM_TryToSmush(avc, CRED() , 1 );
206         else
207 #endif
208             osi_VM_StoreAllSegments(avc);
209     }
210
211     ConvertWToSLock(&avc->lock);
212     
213   /*
214    * Subsequent code expects a sorted list, and it expects all the
215    * chunks in the list to be contiguous, so we need a sort and a
216    * while loop in here, too - but this will work for a first pass...
217    * 92.10.05 - OK, there's a sort in here now.  It's kind of a modified
218    *            bin sort, I guess.  Chunk numbers start with 0
219    *
220    * - Have to get a write lock on xdcache because GetDSlot might need it (if
221    *   the chunk doesn't have a dcache struct).
222    *   This seems like overkill in most cases.
223    * - I'm not sure that it's safe to do "index = .hvNextp", then unlock 
224    *   xdcache, then relock xdcache and try to use index.  It is done
225    *   a lot elsewhere in the CM, but I'm not buying that argument.
226    * - should be able to check IFDataMod without doing the GetDSlot (just
227    *   hold afs_xdcache).  That way, it's easy to do this without the
228    *   writelock on afs_xdcache, and we save unneccessary disk
229    *   operations. I don't think that works, 'cuz the next pointers 
230    *   are still on disk.
231    */
232     origCBs = afs_allCBs;
233
234   retry:
235     maxStoredLength = 0;
236     tlen = avc->m.Length;
237     minj = 0 ; 
238
239     do {
240       bzero ((char *)dcList, NCHUNKSATONCE * sizeof(struct dcache *));
241       high = 0;
242       moredata = FALSE;
243
244       /* lock and start over from beginning of hash chain 
245        * in order to avoid a race condition. */
246       MObtainWriteLock(&afs_xdcache,284);  
247       index = afs_dvhashTbl[hash];
248     
249       for(j=0; index != NULLIDX;) {
250         if ((afs_indexFlags[index] & IFDataMod) &&
251             (afs_indexUnique[index] == avc->fid.Fid.Unique)) {
252           tdc = afs_GetDSlot(index, 0);  /* refcount+1. */
253           if (!FidCmp( &tdc->f.fid, &avc->fid ) && tdc->f.chunk >= minj ) {
254
255             off = tdc->f.chunk - minj;
256             if (off < NCHUNKSATONCE) {
257               if ( dcList[ off ] )
258                 osi_Panic("dclist slot already in use!");
259               dcList[ off ] = tdc;
260               if (off > high) 
261                 high = off;
262               tlen -= tdc->f.chunkBytes; /* shortcut: big win for little files */
263               j++;
264               if (tlen <= 0)
265                 break;
266             }
267             else {
268               moredata = TRUE;
269               lockedPutDCache(tdc);
270               if (j == NCHUNKSATONCE)
271                 break;
272             }
273           } else {
274             lockedPutDCache(tdc);
275           }
276         }
277         index = afs_dvnextTbl[index];
278       }
279     
280       MReleaseWriteLock(&afs_xdcache);
281       /* this guy writes chunks, puts back dcache structs, and bumps newDV */
282       /* "moredata" just says "there are more dirty chunks yet to come".
283        */
284       if (j) {
285         static afs_uint32 lp1 = 10000, lp2 = 10000;
286         struct AFSStoreStatus InStatus;
287         afs_uint32 base, bytes, nchunks;
288         int nomore;
289         unsigned int first;
290         int *shouldwake;
291         struct conn * tc;
292         struct osi_file * tfile;
293         struct rx_call * tcall;
294         extern int afs_defaultAsynchrony;
295         XSTATS_DECLS
296
297         for (bytes = 0, j = 0; !code && j<=high; j++) {
298           if (dcList[j]) {
299             if (!bytes)
300               first = j;
301             bytes += dcList[j]->f.chunkBytes;
302             if ((dcList[j]->f.chunkBytes < afs_OtherCSize)
303                 && (dcList[j]->f.chunk - minj < high)
304                 && dcList[j+1]) {
305                 int sbytes = afs_OtherCSize - dcList[j]->f.chunkBytes;
306                 bytes += sbytes;
307
308
309
310             }
311           }         
312           if (bytes && (j==high || !dcList[j+1])) {
313             /* base = AFS_CHUNKTOBASE(dcList[first]->f.chunk); */
314             base = AFS_CHUNKTOBASE(first + minj) ;
315             /*
316              * 
317              * take a list of dcache structs and send them all off to the server
318              * the list must be in order, and the chunks contiguous.
319              * Note - there is no locking done by this code currently.  For
320              * safety's sake, xdcache could be locked over the entire call.
321              * However, that pretty well ties up all the threads.  Meantime, all
322              * the chunks _MUST_ have their refcounts bumped.
323              * The writes done before a store back will clear setuid-ness
324              * in cache file.
325              * We can permit CacheStoreProc to wake up the user process IFF we 
326              * are doing the last RPC for this close, ie, storing back the last 
327              * set of contiguous chunks of a file.
328              */
329
330             dclist = &dcList[first];
331             nchunks = 1+j-first;
332             nomore = !(moredata || (j!=high));
333             InStatus.ClientModTime = avc->m.Date;
334             InStatus.Mask = AFS_SETMODTIME;
335             if (sync & AFS_SYNC) {
336                 InStatus.Mask |= AFS_FSYNC;
337             }
338             tlen = lmin(avc->m.Length, avc->truncPos);
339
340             do {
341                 stored = 0;
342                 tc = afs_Conn(&avc->fid, areq);
343                 if (tc) {
344 #ifdef RX_ENABLE_LOCKS
345                     AFS_GUNLOCK();
346 #endif /* RX_ENABLE_LOCKS */
347                     tcall = rx_NewCall(tc->id);
348                     code = StartRXAFS_StoreData(tcall, (struct AFSFid *) &avc->fid.Fid,
349                                                 &InStatus, base, bytes, tlen);
350 #ifdef RX_ENABLE_LOCKS
351                     AFS_GLOCK();
352 #endif /* RX_ENABLE_LOCKS */
353                 } else {
354                     code = -1;
355                     tcall = NULL;
356                 }
357                 if (!code) {
358                     XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STOREDATA);
359                     avc->truncPos = AFS_NOTRUNC;
360                 }
361                 for (i = 0; i<nchunks && !code;i++) {
362                     tdc = dclist[i];
363                     if (!tdc) {
364                         afs_warn("afs: missing dcache!\n");
365                         storeallmissing++;
366                         continue; /* panic? */
367                     }
368                     shouldwake = 0;
369                     if (nomore) {
370                        if (avc->asynchrony == -1) {
371                           if (afs_defaultAsynchrony > (bytes-stored)) {
372                              shouldwake = &nomore;
373                           }
374                        } else if ((afs_uint32)avc->asynchrony >= (bytes-stored)) {
375                           shouldwake = &nomore;
376                        }
377                     }
378                     tfile = afs_CFileOpen(tdc->f.inode);
379 #ifndef AFS_NOSTATS
380                     xferP = &(afs_stats_cmfullperf.rpc.fsXferTimes[AFS_STATS_FS_XFERIDX_STOREDATA]);
381                     osi_GetuTime(&xferStartTime);
382
383                     code = afs_CacheStoreProc(tcall, tfile, tdc->f.chunkBytes,
384                                               avc, shouldwake, &bytesToXfer,
385                                               &bytesXferred);
386
387                     osi_GetuTime(&xferStopTime);
388                     (xferP->numXfers)++;
389                     if (!code) {
390                         (xferP->numSuccesses)++;
391                         afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_STOREDATA] += bytesXferred;
392                         (xferP->sumBytes) += (afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_STOREDATA] >> 10);
393                         afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_STOREDATA] &= 0x3FF;
394                         if (bytesXferred < xferP->minBytes)
395                            xferP->minBytes = bytesXferred;
396                         if (bytesXferred > xferP->maxBytes)
397                            xferP->maxBytes = bytesXferred;
398                     
399                       /*
400                        * Tally the size of the object.  Note: we tally the actual size,
401                        * NOT the number of bytes that made it out over the wire.
402                        */
403                         if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET0)
404                             (xferP->count[0])++;
405                         else
406                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET1)
407                                 (xferP->count[1])++;
408                         else
409                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET2)
410                                 (xferP->count[2])++;
411                         else
412                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET3)
413                                 (xferP->count[3])++;
414                         else
415                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET4)
416                                 (xferP->count[4])++;
417                         else
418                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET5)
419                                 (xferP->count[5])++;
420                         else
421                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET6)
422                                 (xferP->count[6])++;
423                         else
424                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET7)
425                                 (xferP->count[7])++;
426                         else
427                             (xferP->count[8])++;
428
429                         afs_stats_GetDiff(elapsedTime, xferStartTime, xferStopTime);
430                         afs_stats_AddTo((xferP->sumTime), elapsedTime);
431                         afs_stats_SquareAddTo((xferP->sqrTime), elapsedTime);
432                         if (afs_stats_TimeLessThan(elapsedTime, (xferP->minTime))) {
433                            afs_stats_TimeAssign((xferP->minTime), elapsedTime);
434                         }
435                         if (afs_stats_TimeGreaterThan(elapsedTime, (xferP->maxTime))) {
436                            afs_stats_TimeAssign((xferP->maxTime), elapsedTime);
437                         }
438                       }
439 #else
440                     code = afs_CacheStoreProc(tcall, tfile, tdc->f.chunkBytes, avc, 
441                                               shouldwake, &lp1, &lp2);
442 #endif /* AFS_NOSTATS */
443                     afs_CFileClose(tfile);
444                     if ((tdc->f.chunkBytes < afs_OtherCSize) && 
445                         (i < (nchunks-1))) {
446                        int bsent, tlen, tlen1=0, sbytes = afs_OtherCSize - tdc->f.chunkBytes;
447                        char *tbuffer = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
448  
449                        while (sbytes > 0) {
450                            tlen = (sbytes > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : sbytes);
451                            bzero(tbuffer, tlen);
452 #ifdef RX_ENABLE_LOCKS
453                            AFS_GUNLOCK();
454 #endif /* RX_ENABLE_LOCKS */
455                            bsent = rx_Write(tcall, tbuffer, tlen);
456 #ifdef RX_ENABLE_LOCKS
457                            AFS_GLOCK();
458 #endif /* RX_ENABLE_LOCKS */
459
460                            if (bsent != tlen) {
461                                code = -33;     /* XXX */
462                                break;
463                            }
464                            sbytes -= tlen;
465                        }
466                        osi_FreeLargeSpace(tbuffer);
467                     }    
468                     stored += tdc->f.chunkBytes;
469
470                     /* ideally, I'd like to unlock the dcache and turn
471                      * off the writing bit here, but that would
472                      * require being able to retry StoreAllSegments in
473                      * the event of a failure. It only really matters
474                      * if user can't read from a 'locked' dcache or
475                      * one which has the writing bit turned on. */
476                 }
477                 if (!code) {
478                     struct AFSFetchStatus OutStatus;
479                     struct AFSVolSync tsync;
480 #ifdef RX_ENABLE_LOCKS
481                     AFS_GUNLOCK();
482 #endif /* RX_ENABLE_LOCKS */
483                     code = EndRXAFS_StoreData(tcall, &OutStatus, &tsync);
484 #ifdef RX_ENABLE_LOCKS
485                     AFS_GLOCK();
486 #endif /* RX_ENABLE_LOCKS */
487                     hadd32(newDV, 1);
488                     XSTATS_END_TIME;
489       
490                     /* Now copy out return params */
491                     UpgradeSToWLock(&avc->lock,28);    /* keep out others for a while */
492                     if (!code) {  /* must wait til RPC completes to be sure of this info */
493                         afs_ProcessFS(avc, &OutStatus, areq);
494                         /* Keep last (max) size of file on server to see if
495                          * we need to call afs_StoreMini to extend the file.
496                          */
497                         if (!moredata)
498                             maxStoredLength = OutStatus.Length;
499
500                     }
501                     ConvertWToSLock(&avc->lock);
502                 }
503                 if (tcall) {
504 #ifdef RX_ENABLE_LOCKS
505                     AFS_GUNLOCK();
506 #endif /* RX_ENABLE_LOCKS */
507                     code = rx_EndCall(tcall, code, avc, base);  
508 #ifdef RX_ENABLE_LOCKS
509                     AFS_GLOCK();
510 #endif /* RX_ENABLE_LOCKS */
511                 }
512             } while (afs_Analyze(tc, code, &avc->fid, areq,
513                                  AFS_STATS_FS_RPCIDX_STOREDATA,
514                                  SHARED_LOCK, (struct cell *)0));
515
516             /* put back all remaining locked dcache entries */  
517             for (i=0; i<nchunks; i++) {
518                 tdc = dclist[i];
519                 if (!code) {
520                     if (afs_indexFlags[tdc->index] & IFDataMod) {
521                         afs_indexFlags[tdc->index] &= ~IFDataMod;
522                         afs_stats_cmperf.cacheCurrDirtyChunks--;
523                         afs_indexFlags[tdc->index] &= ~IFDirtyPages;
524                         if ( sync & AFS_VMSYNC_INVAL )
525                         {
526                             /* since we have invalidated all the pages of this
527                             ** vnode by calling osi_VM_TryToSmush, we can
528                             ** safely mark this dcache entry as not having
529                             ** any pages. This vnode now becomes eligible for
530                             ** reclamation by getDownD.
531                             */
532                             afs_indexFlags[tdc->index] &= ~IFAnyPages;
533                         }
534                     }
535                 }
536                 tdc->f.states &= ~DWriting;  /* correct?*/
537                 tdc->flags |= DFEntryMod;
538                 lockedPutDCache(tdc);
539             }
540
541             if (code) {
542                 for (j++; j<=high; j++)
543                     if ( dcList[j] )
544                         lockedPutDCache(dcList[j]);
545             }
546
547             afs_Trace2(afs_iclSetp, CM_TRACE_STOREALLDCDONE,
548                        ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code);
549             bytes = 0;
550           }
551         }
552       } /* if (j) */
553
554     minj += NCHUNKSATONCE;
555     } while ( !code && moredata ); 
556     
557     UpgradeSToWLock(&avc->lock,29);
558
559   /* send a trivial truncation store if did nothing else */
560   if (code == 0) {
561     /*
562      * Call StoreMini if we haven't written enough data to extend the
563      * file at the fileserver to the client's notion of the file length.
564      */
565     if ((avc->truncPos != AFS_NOTRUNC) ||
566         ((avc->states & CExtendedFile) && (maxStoredLength < avc->m.Length))) {
567       code = afs_StoreMini(avc, areq);
568       if (code == 0)
569         hadd32(newDV, 1);       /* just bumped here, too */
570     }
571     avc->states &= ~CExtendedFile;
572   }
573   
574   /*
575    * Finally, turn off DWriting, turn on DFEntryMod,
576    * update f.versionNo.
577    * A lot of this could be integrated into the loop above 
578    */
579     if (!code) {
580         afs_hyper_t h_unset;
581         hones(h_unset);
582       MObtainWriteLock(&afs_xdcache,285);  /* overkill, but it gets the 
583                                         * lock in case GetDSlot needs it */
584       for(safety = 0, index = afs_dvhashTbl[hash]; 
585           index != NULLIDX && safety < afs_cacheFiles+2;) {
586
587         if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
588           tdc = afs_GetDSlot(index, 0);
589
590           if (!FidCmp(&tdc->f.fid, &avc->fid)) {
591             /* this is the file */
592             /* was code here to clear IFDataMod, but it should only be done
593              * in storedcache and storealldcache.
594              */
595             /* Only increase DV if we had up-to-date data to start with.
596              * Otherwise, we could be falsely upgrading an old chunk
597              * (that we never read) into one labelled with the current
598              * DV #.  Also note that we check that no intervening stores
599              * occurred, otherwise we might mislabel cache information
600              * for a chunk that we didn't store this time
601              */
602              /* Don't update the version number if it's not yet set. */
603             if (code == 0 && (!hsame(tdc->f.versionNo, h_unset))
604                 && (hcmp(tdc->f.versionNo, oldDV) >= 0)) {
605               if ((!(afs_dvhack || foreign) && hsame(avc->m.DataVersion, newDV))
606                   || ((afs_dvhack || foreign) && (origCBs == afs_allCBs)) ) {
607                   /* no error, this is the DV */
608                 hset(tdc->f.versionNo, avc->m.DataVersion);
609                 tdc->flags |= DFEntryMod;
610               }
611             }
612           }
613           lockedPutDCache(tdc);
614         }
615         index = afs_dvnextTbl[index];
616       }
617       MReleaseWriteLock(&afs_xdcache);
618     }
619
620     if (code) {
621         /*
622          * Invalidate chunks after an error for ccores files since
623          * afs_inactive won't be called for these and they won't be
624          * invalidated. Also discard data if it's a permanent error from the
625          * fileserver.
626          */
627         if (areq->permWriteError || (avc->states & (CCore1 | CCore))) {
628             afs_InvalidateAllSegments(avc, 1);
629         }
630     }
631     afs_Trace3(afs_iclSetp, CM_TRACE_STOREALLDONE, ICL_TYPE_POINTER, avc,
632                ICL_TYPE_INT32, avc->m.Length, ICL_TYPE_INT32, code);
633     /* would like a Trace5, but it doesn't exist...*/
634     afs_Trace3(afs_iclSetp, CM_TRACE_AVCLOCKER, ICL_TYPE_POINTER, avc,
635                ICL_TYPE_INT32, avc->lock.wait_states, 
636                ICL_TYPE_INT32, avc->lock.excl_locked);
637     afs_Trace4(afs_iclSetp, CM_TRACE_AVCLOCKEE, ICL_TYPE_POINTER, avc,
638                ICL_TYPE_INT32, avc->lock.wait_states, 
639                ICL_TYPE_INT32, avc->lock.readers_reading, 
640                ICL_TYPE_INT32, avc->lock.num_waiting );
641   
642     /*
643      * Finally, if updated DataVersion matches newDV, we did all of the
644      * stores.  If mapDV indicates that the page cache was flushed up
645      * to when we started the store, then we can relabel them as flushed
646      * as recently as newDV.
647      * Turn off CDirty bit because the stored data is now in sync with server.
648      */
649     if (code == 0 && hcmp(avc->mapDV, oldDV) >= 0) {
650       if ((!(afs_dvhack || foreign) && hsame(avc->m.DataVersion, newDV))
651           || ((afs_dvhack || foreign) && (origCBs == afs_allCBs)) ) {
652           hset(avc->mapDV, newDV);
653           avc->states &= ~CDirty;
654       }
655     }
656     osi_FreeLargeSpace(dcList);
657
658     /* If not the final write a temporary error is ok. */
659     if (code && !areq->permWriteError && !(sync & AFS_LASTSTORE))
660         code = 0;
661
662     return code;
663   
664 } /*afs_StoreAllSegments (new 03/02/94)*/
665
666
667 /*
668  * afs_InvalidateAllSegments
669  *
670  * Description:
671  *      Invalidates all chunks for a given file
672  *
673  * Parameters:
674  *      avc      : Pointer to vcache entry.
675  *      asetLock : If true, we are to set the afs_xdcache lock; otherwise,
676  *                 the caller has already done it.
677  *
678  * Environment:
679  *      For example, called after an error has been detected.  Called
680  *      with avc write-locked.
681  */
682    
683 afs_InvalidateAllSegments(avc, asetLock)
684     struct vcache *avc;
685     int asetLock;
686
687 { /*afs_InvalidateAllSegments*/
688
689     struct dcache *tdc;
690     afs_int32 hash;
691     afs_int32 index;
692
693     AFS_STATCNT(afs_InvalidateAllSegments);
694     afs_Trace2(afs_iclSetp, CM_TRACE_INVALL, ICL_TYPE_POINTER, avc,
695                ICL_TYPE_INT32, avc->m.Length);
696     hash = DVHash(&avc->fid);
697     avc->truncPos = AFS_NOTRUNC;  /* don't truncate later */
698     avc->states &= ~CExtendedFile; /* not any more */
699     ObtainWriteLock(&afs_xcbhash, 459);
700     afs_DequeueCallback(avc);
701     avc->states &= ~(CStatd|CDirty);      /* mark status information as bad, too */
702     ReleaseWriteLock(&afs_xcbhash);
703     if (avc->fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
704         osi_dnlc_purgedp(avc);
705     /* Blow away pages; for now, only for Solaris */
706 #if     (defined(AFS_SUN5_ENV))
707     if (WriteLocked(&avc->lock))
708         osi_ReleaseVM(avc, (struct AFS_UCRED *)0);
709 #endif
710     /*
711      * Block out others from screwing with this table; is a read lock
712      * sufficient?
713      */
714     if (asetLock) MObtainWriteLock(&afs_xdcache,286);
715     for(index = afs_dvhashTbl[hash]; index != NULLIDX;) {
716       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
717         tdc = afs_GetDSlot(index, 0);
718         if (!FidCmp(&tdc->f.fid, &avc->fid)) {
719             /* same file? we'll zap it */
720             if (afs_indexFlags[index] & IFDataMod) {
721                 afs_stats_cmperf.cacheCurrDirtyChunks--;
722                 /* don't write it back */
723                 afs_indexFlags[index] &= ~IFDataMod;
724               }
725             afs_indexFlags[index] &= ~IFAnyPages;
726             ZapDCE(tdc);
727             if (vType(avc) == VDIR) {
728                 DZap(&tdc->f.inode);
729             }
730         }
731         lockedPutDCache(tdc);
732       }
733       index = afs_dvnextTbl[index];
734     }
735     if (asetLock) MReleaseWriteLock(&afs_xdcache);
736     return 0;
737
738 } /*afs_InvalidateAllSegments*/
739
740
741 /*
742  * afs_TruncateAllSegments
743  *
744  * Description:
745  *      Truncate a cache file.
746  *
747  * Parameters:
748  *      avc  : Ptr to vcache entry to truncate.
749  *      alen : Number of bytes to make the file.
750  *      areq : Ptr to request structure.
751  *
752  * Environment:
753  *      Called with avc write-locked; in VFS40 systems, pvnLock is also
754  *      held.
755  */
756 afs_TruncateAllSegments(avc, alen, areq, acred)
757     afs_int32 alen;
758     register struct vcache *avc;
759     struct vrequest *areq;
760     struct AFS_UCRED *acred;
761 { /*afs_TruncateAllSegments*/
762
763     register struct dcache *tdc;
764     register afs_int32 code;
765     register afs_int32 index;
766     afs_int32 newSize;
767
768     AFS_STATCNT(afs_TruncateAllSegments);
769     avc->m.Date = osi_Time();
770     if (alen >= avc->m.Length) {
771         /*
772          * Special speedup since Sun's vm extends the file this way;
773          * we've never written to the file thus we can just set the new
774          * length and avoid the needless calls below.
775          * Also used for ftruncate calls which can extend the file.
776          * To completely minimize the possible extra StoreMini RPC, we really
777          * should keep the ExtendedPos as well and clear this flag if we
778          * truncate below that value before we store the file back.
779          */
780         avc->states |= CExtendedFile;
781         avc->m.Length = alen;
782         afs_Trace3(afs_iclSetp, CM_TRACE_TRUNCALL1, ICL_TYPE_POINTER, avc,
783                    ICL_TYPE_INT32, avc->m.Length, ICL_TYPE_INT32, alen);
784         return 0;
785     }
786
787     afs_Trace3(afs_iclSetp, CM_TRACE_TRUNCALL2, ICL_TYPE_POINTER, avc,
788                ICL_TYPE_INT32, avc->m.Length, ICL_TYPE_INT32, alen);
789
790 #if     (defined(AFS_SUN5_ENV))
791
792     /* Zero unused portion of last page */
793     osi_VM_PreTruncate(avc, alen, acred);
794
795 #endif
796
797 #if     (defined(AFS_SUN5_ENV))
798     ObtainWriteLock(&avc->vlock, 546);
799     avc->activeV++;             /* Block new getpages */
800     ReleaseWriteLock(&avc->vlock);
801 #endif
802
803     ReleaseWriteLock(&avc->lock);
804     AFS_GUNLOCK();
805
806     /* Flush pages beyond end-of-file. */
807     osi_VM_Truncate(avc, alen, acred);
808
809     AFS_GLOCK();
810     ObtainWriteLock(&avc->lock,79);
811
812     avc->m.Length = alen;
813     
814     if (alen < avc->truncPos) avc->truncPos = alen;
815     code = DVHash(&avc->fid);
816     /* block out others from screwing with this table */
817     MObtainWriteLock(&afs_xdcache,287);
818     for(index = afs_dvhashTbl[code]; index != NULLIDX;) {
819       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
820         tdc = afs_GetDSlot(index, 0);
821         if (!FidCmp(&tdc->f.fid, &avc->fid)) {
822             /* same file, and modified, we'll store it back */
823             newSize = alen - AFS_CHUNKTOBASE(tdc->f.chunk);
824             if (newSize < 0) newSize = 0;
825             if (newSize < tdc->f.chunkBytes) {
826                 register struct osi_file *tfile;
827                 tfile = afs_CFileOpen(tdc->f.inode);
828                 afs_CFileTruncate(tfile, newSize);
829                 afs_CFileClose(tfile);
830                 afs_AdjustSize(tdc, newSize);
831             }
832         }
833         lockedPutDCache(tdc);
834       }
835       index = afs_dvnextTbl[index];
836     }
837 #if     (defined(AFS_SUN5_ENV))
838     ObtainWriteLock(&avc->vlock, 547);
839     if (--avc->activeV == 0 && (avc->vstates & VRevokeWait)) {
840         avc->vstates &= ~VRevokeWait;
841         afs_osi_Wakeup((char *)&avc->vstates);
842     }
843     ReleaseWriteLock(&avc->vlock);
844 #endif
845     MReleaseWriteLock(&afs_xdcache);
846     return 0;
847
848 } /*afs_TruncateAllSegments*/
849
850