Delay calling afs_ProcessFS() and upgrading the shared vcache lock
[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_size_t tlen, base = 0;
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             RX_AFS_GUNLOCK();
78 retry:
79             tcall = rx_NewCall(tc->id);
80             RX_AFS_GLOCK();
81             /* Set the client mod time since we always want the file
82              * to have the client's mod time and not the server's one
83              * (to avoid problems with make, etc.) It almost always
84              * works fine with standard afs because them server/client
85              * times are in sync and more importantly this storemini
86              * it's a special call that would typically be followed by
87              * the proper store-data or store-status calls.
88              */
89             InStatus.Mask = AFS_SETMODTIME;
90             InStatus.ClientModTime = avc->m.Date;
91             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STOREDATA);
92             afs_Trace4(afs_iclSetp, CM_TRACE_STOREDATA64, 
93                 ICL_TYPE_FID, &avc->fid.Fid,
94                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(base), 
95                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(tlen), 
96                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length));
97             RX_AFS_GUNLOCK();
98 #ifdef AFS_64BIT_CLIENT
99             if (!afs_serverHasNo64Bit(tc)) {
100                 code = StartRXAFS_StoreData64(tcall,
101                                         (struct AFSFid *)&avc->fid.Fid,
102                                         &InStatus, avc->m.Length,
103                                         base, tlen);
104             } else {
105                 afs_int32 l1, l2;
106                 l1 = avc->m.Length;
107                 l2 = tlen;
108                 code = StartRXAFS_StoreData(tcall,
109                                         (struct AFSFid *)&avc->fid.Fid,
110                                         &InStatus, l1, 0, l2);
111             }
112 #else /* AFS_64BIT_CLIENT */
113             code = StartRXAFS_StoreData(tcall,
114                                         (struct AFSFid *)&avc->fid.Fid,
115                                         &InStatus, avc->m.Length, 0, tlen);
116 #endif /* AFS_64BIT_CLIENT */
117             if (code == 0) {
118                 code = EndRXAFS_StoreData(tcall, &OutStatus, &tsync);
119 #ifdef AFS_64BIT_CLIENT
120                 if (code == RXGEN_OPCODE) {
121                     afs_serverSetNo64Bit(tc);
122                     code = rx_EndCall(tcall, code);
123                     goto retry;
124                 }
125 #endif /* AFS_64BIT_CLIENT */
126             }
127             code = rx_EndCall(tcall, code);
128             RX_AFS_GLOCK();
129             XSTATS_END_TIME;
130         }
131         else code = -1;
132     } while
133         (afs_Analyze(tc, code, &avc->fid, areq,
134                      AFS_STATS_FS_RPCIDX_STOREDATA,
135                      SHARED_LOCK, (struct cell *)0));
136
137     if (code == 0) {
138         afs_ProcessFS(avc, &OutStatus, areq);
139     }
140     else {
141         /* blew it away */
142         afs_InvalidateAllSegments(avc);
143     }
144     return code;
145
146 } /*afs_StoreMini*/
147
148 unsigned int storeallmissing = 0;
149 #define lmin(a,b) (((a) < (b)) ? (a) : (b))
150 /*
151  * afs_StoreAllSegments
152  *
153  * Description:
154  *      Stores all modified segments back to server
155  *
156  * Parameters:
157  *      avc  : Pointer to vcache entry.
158  *      areq : Pointer to request structure.
159  *
160  * Environment:
161  *      Called with avc write-locked.
162  */
163 #if defined (AFS_HPUX_ENV) || defined(AFS_ULTRIX_ENV)
164 int NCHUNKSATONCE = 3;
165 #else
166 int NCHUNKSATONCE = 64 ;
167 #endif
168 int afs_dvhack=0;
169
170
171 afs_StoreAllSegments(avc, areq, sync)
172     register struct vcache *avc;
173     struct vrequest *areq;
174     int sync;
175
176 { /*afs_StoreAllSegments*/
177     register struct dcache *tdc;
178     register afs_int32 code=0;
179     register afs_int32 index;
180     register afs_int32 origCBs, foreign=0;
181     int hash, stored;
182     afs_hyper_t newDV, oldDV;   /* DV when we start, and finish, respectively */
183     struct dcache **dcList, **dclist;
184     unsigned int i, j, minj, maxj, moredata, high, off;
185     afs_size_t tlen;
186     afs_size_t maxStoredLength; /* highest offset we've written to server. */
187     int safety;
188 #ifndef AFS_NOSTATS
189     struct afs_stats_xferData *xferP;   /* Ptr to this op's xfer struct */
190     osi_timeval_t  xferStartTime,       /*FS xfer start time*/
191                    xferStopTime;        /*FS xfer stop time*/
192     afs_size_t bytesToXfer;                     /* # bytes to xfer*/
193     afs_size_t bytesXferred;                    /* # bytes actually xferred*/
194 #endif /* AFS_NOSTATS */
195
196
197     AFS_STATCNT(afs_StoreAllSegments);
198  
199     hset(oldDV, avc->m.DataVersion);
200     hset(newDV, avc->m.DataVersion);
201     hash = DVHash(&avc->fid);
202     foreign = (avc->states & CForeign);
203     dcList = (struct dcache **) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
204     afs_Trace2(afs_iclSetp, CM_TRACE_STOREALL, ICL_TYPE_POINTER, avc,
205                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length));
206 #if !defined(AFS_AIX32_ENV) && !defined(AFS_SGI65_ENV)
207     /* In the aix vm implementation we need to do the vm_writep even
208      * on the memcache case since that's we adjust the file's size
209      * and finish flushing partial vm pages.
210      */
211     if (cacheDiskType != AFS_FCACHE_TYPE_MEM) 
212 #endif /* !AFS_AIX32_ENV && !AFS_SGI65_ENV */
213     {
214         /* If we're not diskless, reading a file may stress the VM
215          * system enough to cause a pageout, and this vnode would be
216          * locked when the pageout occurs.  We can prevent this problem
217          * by making sure all dirty pages are already flushed.  We don't
218          * do this when diskless because reading a diskless (i.e.
219          * memory-resident) chunk doesn't require using new VM, and we
220          * also don't want to dump more dirty data into a diskless cache,
221          * since they're smaller, and we might exceed its available
222          * space.
223          */
224 #if     defined(AFS_SUN5_ENV)
225         if ( sync & AFS_VMSYNC_INVAL) /* invalidate VM pages */
226             osi_VM_TryToSmush(avc, CRED() , 1 );
227         else
228 #endif
229             osi_VM_StoreAllSegments(avc);
230     }
231
232     ConvertWToSLock(&avc->lock);
233     
234   /*
235    * Subsequent code expects a sorted list, and it expects all the
236    * chunks in the list to be contiguous, so we need a sort and a
237    * while loop in here, too - but this will work for a first pass...
238    * 92.10.05 - OK, there's a sort in here now.  It's kind of a modified
239    *            bin sort, I guess.  Chunk numbers start with 0
240    *
241    * - Have to get a write lock on xdcache because GetDSlot might need it (if
242    *   the chunk doesn't have a dcache struct).
243    *   This seems like overkill in most cases.
244    * - I'm not sure that it's safe to do "index = .hvNextp", then unlock 
245    *   xdcache, then relock xdcache and try to use index.  It is done
246    *   a lot elsewhere in the CM, but I'm not buying that argument.
247    * - should be able to check IFDataMod without doing the GetDSlot (just
248    *   hold afs_xdcache).  That way, it's easy to do this without the
249    *   writelock on afs_xdcache, and we save unneccessary disk
250    *   operations. I don't think that works, 'cuz the next pointers 
251    *   are still on disk.
252    */
253     origCBs = afs_allCBs;
254
255   retry:
256     maxStoredLength = 0;
257     tlen = avc->m.Length;
258     minj = 0 ; 
259
260     do {
261       memset((char *)dcList, 0, NCHUNKSATONCE * sizeof(struct dcache *));
262       high = 0;
263       moredata = FALSE;
264
265       /* lock and start over from beginning of hash chain 
266        * in order to avoid a race condition. */
267       MObtainWriteLock(&afs_xdcache,284);  
268       index = afs_dvhashTbl[hash];
269     
270       for(j=0; index != NULLIDX;) {
271         if ((afs_indexFlags[index] & IFDataMod) &&
272             (afs_indexUnique[index] == avc->fid.Fid.Unique)) {
273           tdc = afs_GetDSlot(index, 0);  /* refcount+1. */
274           ReleaseReadLock(&tdc->tlock);
275           if (!FidCmp( &tdc->f.fid, &avc->fid ) && tdc->f.chunk >= minj ) {
276             off = tdc->f.chunk - minj;
277             if (off < NCHUNKSATONCE) {
278               if ( dcList[ off ] )
279                 osi_Panic("dclist slot already in use!");
280               dcList[ off ] = tdc;
281               if (off > high) 
282                 high = off;
283               j++;
284               /* DCLOCKXXX: chunkBytes is protected by tdc->lock which we
285                * can't grab here, due to lock ordering with afs_xdcache.
286                * So, disable this shortcut for now.  -- kolya 2001-10-13
287                */
288               /* shortcut: big win for little files */
289               /* tlen -= tdc->f.chunkBytes;
290                * if (tlen <= 0)
291                *    break;
292                */
293             }
294             else {
295               moredata = TRUE;
296               afs_PutDCache(tdc);
297               if (j == NCHUNKSATONCE)
298                 break;
299             }
300           } else {
301             afs_PutDCache(tdc);
302           }
303         }
304         index = afs_dvnextTbl[index];
305       }
306       MReleaseWriteLock(&afs_xdcache);
307
308       /* this guy writes chunks, puts back dcache structs, and bumps newDV */
309       /* "moredata" just says "there are more dirty chunks yet to come".
310        */
311       if (j) {
312         static afs_uint32 lp1 = 10000, lp2 = 10000;
313         struct AFSStoreStatus InStatus;
314         struct AFSFetchStatus OutStatus;
315         int doProcessFS = 0;
316         afs_size_t base, bytes;
317         afs_uint32 nchunks;
318         int nomore;
319         unsigned int first;
320         int *shouldwake;
321         struct conn * tc;
322         struct osi_file * tfile;
323         struct rx_call * tcall;
324         extern int afs_defaultAsynchrony;
325         XSTATS_DECLS
326
327         for (bytes = 0, j = 0; !code && j<=high; j++) {
328           if (dcList[j]) {
329             ObtainSharedLock(&(dcList[j]->lock), 629);
330             if (!bytes)
331               first = j;
332             bytes += dcList[j]->f.chunkBytes;
333             if ((dcList[j]->f.chunkBytes < afs_OtherCSize)
334                 && (dcList[j]->f.chunk - minj < high)
335                 && dcList[j+1]) {
336                 int sbytes = afs_OtherCSize - dcList[j]->f.chunkBytes;
337                 bytes += sbytes;
338             }
339           }         
340           if (bytes && (j==high || !dcList[j+1])) {
341             /* base = AFS_CHUNKTOBASE(dcList[first]->f.chunk); */
342             base = AFS_CHUNKTOBASE(first + minj) ;
343             /*
344              * 
345              * take a list of dcache structs and send them all off to the server
346              * the list must be in order, and the chunks contiguous.
347              * Note - there is no locking done by this code currently.  For
348              * safety's sake, xdcache could be locked over the entire call.
349              * However, that pretty well ties up all the threads.  Meantime, all
350              * the chunks _MUST_ have their refcounts bumped.
351              * The writes done before a store back will clear setuid-ness
352              * in cache file.
353              * We can permit CacheStoreProc to wake up the user process IFF we 
354              * are doing the last RPC for this close, ie, storing back the last 
355              * set of contiguous chunks of a file.
356              */
357
358             dclist = &dcList[first];
359             nchunks = 1+j-first;
360             nomore = !(moredata || (j!=high));
361             InStatus.ClientModTime = avc->m.Date;
362             InStatus.Mask = AFS_SETMODTIME;
363             if (sync & AFS_SYNC) {
364                 InStatus.Mask |= AFS_FSYNC;
365             }
366             tlen = lmin(avc->m.Length, avc->truncPos);
367             afs_Trace4(afs_iclSetp, CM_TRACE_STOREDATA64, 
368                 ICL_TYPE_FID, &avc->fid.Fid,
369                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(base), 
370                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(bytes), 
371                 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(tlen));
372
373             do {
374                 stored = 0;
375                 tc = afs_Conn(&avc->fid, areq);
376                 if (tc) {
377 restart:
378                     RX_AFS_GUNLOCK();
379                     tcall = rx_NewCall(tc->id);
380 #ifdef AFS_64BIT_CLIENT
381                     if (!afs_serverHasNo64Bit(tc)) {
382                         code = StartRXAFS_StoreData64(tcall, 
383                                 (struct AFSFid *) &avc->fid.Fid,
384                                 &InStatus, base, bytes, tlen);
385                     } else {
386                         if (tlen > 0xFFFFFFFF) {
387                             code = EFBIG;
388                         } else {
389                             afs_int32 t1, t2, t3;
390                             t1 = base;
391                             t2 = bytes;
392                             t3 = tlen;
393                             code = StartRXAFS_StoreData(tcall, 
394                                         (struct AFSFid *) &avc->fid.Fid,
395                                         &InStatus, t1, t2, t3);
396                         }
397                     }
398 #else /* AFS_64BIT_CLIENT */
399                     code = StartRXAFS_StoreData(tcall, (struct AFSFid *) &avc->fid.Fid,
400                                                 &InStatus, base, bytes, tlen);
401 #endif /* AFS_64BIT_CLIENT */
402                     RX_AFS_GLOCK();
403                 } else {
404                     code = -1;
405                     tcall = NULL;
406                 }
407                 if (!code) {
408                     XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STOREDATA);
409                     avc->truncPos = AFS_NOTRUNC;
410                 }
411                 for (i = 0; i<nchunks && !code;i++) {
412                     tdc = dclist[i];
413                     if (!tdc) {
414                         afs_warn("afs: missing dcache!\n");
415                         storeallmissing++;
416                         continue; /* panic? */
417                     }
418                     afs_Trace4(afs_iclSetp, CM_TRACE_STOREALL2,
419                         ICL_TYPE_POINTER, avc, 
420                         ICL_TYPE_INT32, tdc->f.chunk,
421                         ICL_TYPE_INT32, tdc->index,
422                         ICL_TYPE_INT32, tdc->f.inode);
423                     shouldwake = 0;
424                     if (nomore) {
425                        if (avc->asynchrony == -1) {
426                           if (afs_defaultAsynchrony > (bytes-stored)) {
427                              shouldwake = &nomore;
428                           }
429                        } else if ((afs_uint32)avc->asynchrony >= (bytes-stored)) {
430                           shouldwake = &nomore;
431                        }
432                     }
433                     tfile = afs_CFileOpen(tdc->f.inode);
434 #ifndef AFS_NOSTATS
435                     xferP = &(afs_stats_cmfullperf.rpc.fsXferTimes[AFS_STATS_FS_XFERIDX_STOREDATA]);
436                     osi_GetuTime(&xferStartTime);
437
438                     code = afs_CacheStoreProc(tcall, tfile, tdc->f.chunkBytes,
439                                               avc, shouldwake, &bytesToXfer,
440                                               &bytesXferred);
441
442                     osi_GetuTime(&xferStopTime);
443                     (xferP->numXfers)++;
444                     if (!code) {
445                         (xferP->numSuccesses)++;
446                         afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_STOREDATA] += bytesXferred;
447                         (xferP->sumBytes) += (afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_STOREDATA] >> 10);
448                         afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_STOREDATA] &= 0x3FF;
449                         if (bytesXferred < xferP->minBytes)
450                            xferP->minBytes = bytesXferred;
451                         if (bytesXferred > xferP->maxBytes)
452                            xferP->maxBytes = bytesXferred;
453                     
454                       /*
455                        * Tally the size of the object.  Note: we tally the actual size,
456                        * NOT the number of bytes that made it out over the wire.
457                        */
458                         if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET0)
459                             (xferP->count[0])++;
460                         else
461                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET1)
462                                 (xferP->count[1])++;
463                         else
464                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET2)
465                                 (xferP->count[2])++;
466                         else
467                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET3)
468                                 (xferP->count[3])++;
469                         else
470                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET4)
471                                 (xferP->count[4])++;
472                         else
473                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET5)
474                                 (xferP->count[5])++;
475                         else
476                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET6)
477                                 (xferP->count[6])++;
478                         else
479                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET7)
480                                 (xferP->count[7])++;
481                         else
482                             (xferP->count[8])++;
483
484                         afs_stats_GetDiff(elapsedTime, xferStartTime, xferStopTime);
485                         afs_stats_AddTo((xferP->sumTime), elapsedTime);
486                         afs_stats_SquareAddTo((xferP->sqrTime), elapsedTime);
487                         if (afs_stats_TimeLessThan(elapsedTime, (xferP->minTime))) {
488                            afs_stats_TimeAssign((xferP->minTime), elapsedTime);
489                         }
490                         if (afs_stats_TimeGreaterThan(elapsedTime, (xferP->maxTime))) {
491                            afs_stats_TimeAssign((xferP->maxTime), elapsedTime);
492                         }
493                       }
494 #else
495                     code = afs_CacheStoreProc(tcall, tfile, tdc->f.chunkBytes, 
496                                                 avc, shouldwake, &lp1, &lp2);
497 #endif /* AFS_NOSTATS */
498                     afs_CFileClose(tfile);
499 #ifdef AFS_64BIT_CLIENT
500                     if (code == RXGEN_OPCODE) {
501                         afs_serverSetNo64Bit(tc);
502                         goto restart;
503                     }
504 #endif /* AFS_64BIT_CLIENT */
505                     if ((tdc->f.chunkBytes < afs_OtherCSize) && 
506                         (i < (nchunks-1))) {
507                        int bsent, tlen, tlen1=0, sbytes = afs_OtherCSize - tdc->f.chunkBytes;
508                        char *tbuffer = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
509  
510                        while (sbytes > 0) {
511                            tlen = (sbytes > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : sbytes);
512                            memset(tbuffer, 0, tlen);
513                            RX_AFS_GUNLOCK();
514                            bsent = rx_Write(tcall, tbuffer, tlen);
515                            RX_AFS_GLOCK();
516
517                            if (bsent != tlen) {
518                                code = -33;     /* XXX */
519                                break;
520                            }
521                            sbytes -= tlen;
522                        }
523                        osi_FreeLargeSpace(tbuffer);
524                     }    
525                     stored += tdc->f.chunkBytes;
526
527                     /* ideally, I'd like to unlock the dcache and turn
528                      * off the writing bit here, but that would
529                      * require being able to retry StoreAllSegments in
530                      * the event of a failure. It only really matters
531                      * if user can't read from a 'locked' dcache or
532                      * one which has the writing bit turned on. */
533                 }
534                 if (!code) {
535                     struct AFSVolSync tsync;
536                     RX_AFS_GUNLOCK();
537                     code = EndRXAFS_StoreData(tcall, &OutStatus, &tsync);
538                     RX_AFS_GLOCK();
539                     hadd32(newDV, 1);
540                     XSTATS_END_TIME;
541                     if (!code) doProcessFS = 1; /* Flag to run afs_ProcessFS() later on */
542                 }
543                 if (tcall) {
544                     RX_AFS_GUNLOCK();
545                     code = rx_EndCall(tcall, code, avc, base);  
546                     RX_AFS_GLOCK();
547                 }
548             } while (afs_Analyze(tc, code, &avc->fid, areq,
549                                  AFS_STATS_FS_RPCIDX_STOREDATA,
550                                  SHARED_LOCK, (struct cell *)0));
551
552             /* put back all remaining locked dcache entries */  
553             for (i=0; i<nchunks; i++) {
554                 tdc = dclist[i];
555                 if (!code) {
556                     if (afs_indexFlags[tdc->index] & IFDataMod) {
557                         /*
558                          * LOCKXXX -- should hold afs_xdcache(W) when
559                          * modifying afs_indexFlags.
560                          */
561                         afs_indexFlags[tdc->index] &= ~IFDataMod;
562                         afs_stats_cmperf.cacheCurrDirtyChunks--;
563                         afs_indexFlags[tdc->index] &= ~IFDirtyPages;
564                         if ( sync & AFS_VMSYNC_INVAL )
565                         {
566                             /* since we have invalidated all the pages of this
567                             ** vnode by calling osi_VM_TryToSmush, we can
568                             ** safely mark this dcache entry as not having
569                             ** any pages. This vnode now becomes eligible for
570                             ** reclamation by getDownD.
571                             */
572                             afs_indexFlags[tdc->index] &= ~IFAnyPages;
573                         }
574                     }
575                 }
576                 UpgradeSToWLock(&tdc->lock, 628);
577                 tdc->f.states &= ~DWriting;  /* correct?*/
578                 tdc->dflags |= DFEntryMod;
579                 ReleaseWriteLock(&tdc->lock);
580                 afs_PutDCache(tdc);
581                 /* Mark the entry as released */
582                 dclist[i] = NULL;
583             }
584
585             if (doProcessFS) {
586                 /* Now copy out return params */
587                 UpgradeSToWLock(&avc->lock,28);    /* keep out others for a while */
588                 afs_ProcessFS(avc, &OutStatus, areq);
589                 /* Keep last (max) size of file on server to see if
590                  * we need to call afs_StoreMini to extend the file.
591                  */
592                 if (!moredata)
593                     maxStoredLength = OutStatus.Length;
594                 ConvertWToSLock(&avc->lock);
595                 doProcessFS = 0;
596             }
597
598             if (code) {
599                 for (j++; j<=high; j++) {
600                     if ( dcList[j] ) {
601                         ReleaseSharedLock(&(dcList[j]->lock));
602                         afs_PutDCache(dcList[j]);
603                         /* Releasing entry */
604                         dcList[j] = NULL;
605                     }
606                 }
607             }
608
609             afs_Trace2(afs_iclSetp, CM_TRACE_STOREALLDCDONE,
610                        ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code);
611             bytes = 0;
612           }
613         }
614         
615         /* Release any zero-length dcache entries in our interval
616          * that we locked but didn't store back above.
617          */
618         for (j = 0; j<=high; j++) {
619           tdc = dcList[j];
620           if (tdc) {
621             osi_Assert(tdc->f.chunkBytes == 0);
622             ReleaseSharedLock(&tdc->lock);
623             afs_PutDCache(tdc);
624           }
625         }
626       } /* if (j) */
627
628     minj += NCHUNKSATONCE;
629     } while ( !code && moredata ); 
630     
631     UpgradeSToWLock(&avc->lock,29);
632
633   /* send a trivial truncation store if did nothing else */
634   if (code == 0) {
635     /*
636      * Call StoreMini if we haven't written enough data to extend the
637      * file at the fileserver to the client's notion of the file length.
638      */
639     if ((avc->truncPos != AFS_NOTRUNC) ||
640         ((avc->states & CExtendedFile) && (maxStoredLength < avc->m.Length))) {
641       code = afs_StoreMini(avc, areq);
642       if (code == 0)
643         hadd32(newDV, 1);       /* just bumped here, too */
644     }
645     avc->states &= ~CExtendedFile;
646   }
647   
648   /*
649    * Finally, turn off DWriting, turn on DFEntryMod,
650    * update f.versionNo.
651    * A lot of this could be integrated into the loop above 
652    */
653     if (!code) {
654       afs_hyper_t h_unset;
655       hones(h_unset);
656
657       minj = 0;
658
659       do {
660         moredata = FALSE;
661         memset((char *)dcList, 0, NCHUNKSATONCE * sizeof(struct dcache *));
662
663         /* overkill, but it gets the lock in case GetDSlot needs it */
664         MObtainWriteLock(&afs_xdcache,285);
665
666         for(j = 0, safety = 0, index = afs_dvhashTbl[hash]; 
667             index != NULLIDX && safety < afs_cacheFiles+2;) {
668
669             if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
670                 tdc = afs_GetDSlot(index, 0);
671                 ReleaseReadLock(&tdc->tlock);
672
673                 if (!FidCmp(&tdc->f.fid, &avc->fid) && tdc->f.chunk >= minj) {
674                     off = tdc->f.chunk - minj;
675                     if (off < NCHUNKSATONCE) {
676                         /* this is the file, and the correct chunk range */
677                         if (j >= NCHUNKSATONCE)
678                             osi_Panic("Too many dcache entries in range\n");
679                         dcList[j++] = tdc;
680                     } else {
681                         moredata = TRUE;
682                         afs_PutDCache(tdc);
683                         if (j == NCHUNKSATONCE)
684                             break;
685                     }
686                 } else {
687                     afs_PutDCache(tdc);
688                 }
689             }
690
691             index = afs_dvnextTbl[index];
692         }
693         MReleaseWriteLock(&afs_xdcache);
694
695         for (i=0; i<j; i++) {
696             /* Iterate over the dcache entries we collected above */
697             tdc = dcList[i];
698             ObtainSharedLock(&tdc->lock, 677);
699
700             /* was code here to clear IFDataMod, but it should only be done
701              * in storedcache and storealldcache.
702              */
703             /* Only increase DV if we had up-to-date data to start with.
704              * Otherwise, we could be falsely upgrading an old chunk
705              * (that we never read) into one labelled with the current
706              * DV #.  Also note that we check that no intervening stores
707              * occurred, otherwise we might mislabel cache information
708              * for a chunk that we didn't store this time
709              */
710              /* Don't update the version number if it's not yet set. */
711             if (!hsame(tdc->f.versionNo, h_unset) &&
712                 hcmp(tdc->f.versionNo, oldDV) >= 0) {
713
714                 if ((!(afs_dvhack || foreign) && hsame(avc->m.DataVersion, newDV))
715                     || ((afs_dvhack || foreign) && (origCBs == afs_allCBs)) ) {
716                     /* no error, this is the DV */
717
718                     UpgradeSToWLock(&tdc->lock, 678);
719                     hset(tdc->f.versionNo, avc->m.DataVersion);
720                     tdc->dflags |= DFEntryMod;
721                     ConvertWToSLock(&tdc->lock);
722                 }
723             }
724
725             ReleaseSharedLock(&tdc->lock);
726             afs_PutDCache(tdc);
727         }
728
729         minj += NCHUNKSATONCE;
730
731       } while (moredata);
732     }
733
734     if (code) {
735         /*
736          * Invalidate chunks after an error for ccores files since
737          * afs_inactive won't be called for these and they won't be
738          * invalidated. Also discard data if it's a permanent error from the
739          * fileserver.
740          */
741         if (areq->permWriteError || (avc->states & (CCore1 | CCore))) {
742             afs_InvalidateAllSegments(avc);
743         }
744     }
745     afs_Trace3(afs_iclSetp, CM_TRACE_STOREALLDONE, ICL_TYPE_POINTER, avc,
746                ICL_TYPE_INT32, avc->m.Length, ICL_TYPE_INT32, code);
747     /* would like a Trace5, but it doesn't exist...*/
748     afs_Trace3(afs_iclSetp, CM_TRACE_AVCLOCKER, ICL_TYPE_POINTER, avc,
749                ICL_TYPE_INT32, avc->lock.wait_states, 
750                ICL_TYPE_INT32, avc->lock.excl_locked);
751     afs_Trace4(afs_iclSetp, CM_TRACE_AVCLOCKEE, ICL_TYPE_POINTER, avc,
752                ICL_TYPE_INT32, avc->lock.wait_states, 
753                ICL_TYPE_INT32, avc->lock.readers_reading, 
754                ICL_TYPE_INT32, avc->lock.num_waiting );
755   
756     /*
757      * Finally, if updated DataVersion matches newDV, we did all of the
758      * stores.  If mapDV indicates that the page cache was flushed up
759      * to when we started the store, then we can relabel them as flushed
760      * as recently as newDV.
761      * Turn off CDirty bit because the stored data is now in sync with server.
762      */
763     if (code == 0 && hcmp(avc->mapDV, oldDV) >= 0) {
764       if ((!(afs_dvhack || foreign) && hsame(avc->m.DataVersion, newDV))
765           || ((afs_dvhack || foreign) && (origCBs == afs_allCBs)) ) {
766           hset(avc->mapDV, newDV);
767           avc->states &= ~CDirty;
768       }
769     }
770     osi_FreeLargeSpace(dcList);
771
772     /* If not the final write a temporary error is ok. */
773     if (code && !areq->permWriteError && !(sync & AFS_LASTSTORE))
774         code = 0;
775
776     return code;
777   
778 } /*afs_StoreAllSegments (new 03/02/94)*/
779
780
781 /*
782  * afs_InvalidateAllSegments
783  *
784  * Description:
785  *      Invalidates all chunks for a given file
786  *
787  * Parameters:
788  *      avc      : Pointer to vcache entry.
789  *
790  * Environment:
791  *      For example, called after an error has been detected.  Called
792  *      with avc write-locked, and afs_xdcache unheld.
793  */
794    
795 afs_InvalidateAllSegments(avc)
796     struct vcache *avc;
797
798 { /*afs_InvalidateAllSegments*/
799
800     struct dcache *tdc;
801     afs_int32 hash;
802     afs_int32 index;
803     struct dcache **dcList;
804     int i, dcListMax, dcListCount;
805
806     AFS_STATCNT(afs_InvalidateAllSegments);
807     afs_Trace2(afs_iclSetp, CM_TRACE_INVALL, ICL_TYPE_POINTER, avc,
808                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length));
809     hash = DVHash(&avc->fid);
810     avc->truncPos = AFS_NOTRUNC;  /* don't truncate later */
811     avc->states &= ~CExtendedFile; /* not any more */
812     ObtainWriteLock(&afs_xcbhash, 459);
813     afs_DequeueCallback(avc);
814     avc->states &= ~(CStatd|CDirty); /* mark status information as bad, too */
815     ReleaseWriteLock(&afs_xcbhash);
816     if (avc->fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
817         osi_dnlc_purgedp(avc);
818     /* Blow away pages; for now, only for Solaris */
819 #if     (defined(AFS_SUN5_ENV))
820     if (WriteLocked(&avc->lock))
821         osi_ReleaseVM(avc, (struct AFS_UCRED *)0);
822 #endif
823     /*
824      * Block out others from screwing with this table; is a read lock
825      * sufficient?
826      */
827     MObtainWriteLock(&afs_xdcache,286);
828     dcListMax = 0;
829
830     for(index = afs_dvhashTbl[hash]; index != NULLIDX;) {
831         if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
832             tdc = afs_GetDSlot(index, 0);
833             ReleaseReadLock(&tdc->tlock);
834             if (!FidCmp(&tdc->f.fid, &avc->fid))
835                 dcListMax++;
836             afs_PutDCache(tdc);
837         }
838         index = afs_dvnextTbl[index];
839     }
840
841     dcList = osi_Alloc(dcListMax * sizeof(struct dcache *));
842     dcListCount = 0;
843
844     for(index = afs_dvhashTbl[hash]; index != NULLIDX;) {
845         if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
846             tdc = afs_GetDSlot(index, 0);
847             ReleaseReadLock(&tdc->tlock);
848             if (!FidCmp(&tdc->f.fid, &avc->fid)) {
849                 /* same file? we'll zap it */
850                 if (afs_indexFlags[index] & IFDataMod) {
851                     afs_stats_cmperf.cacheCurrDirtyChunks--;
852                     /* don't write it back */
853                     afs_indexFlags[index] &= ~IFDataMod;
854                 }
855                 afs_indexFlags[index] &= ~IFAnyPages;
856                 if (dcListCount < dcListMax)
857                     dcList[dcListCount++] = tdc;
858                 else
859                     afs_PutDCache(tdc);
860             } else {
861                 afs_PutDCache(tdc);
862             }
863         }
864         index = afs_dvnextTbl[index];
865     }
866     MReleaseWriteLock(&afs_xdcache);
867
868     for (i=0; i<dcListCount; i++) {
869         tdc = dcList[i];
870
871         ObtainWriteLock(&tdc->lock, 679);
872         ZapDCE(tdc);
873         if (vType(avc) == VDIR)
874             DZap(&tdc->f.inode);
875         ReleaseWriteLock(&tdc->lock);
876         afs_PutDCache(tdc);
877     }
878
879     osi_Free(dcList, dcListMax * sizeof(struct dcache *));
880
881     return 0;
882
883 } /*afs_InvalidateAllSegments*/
884
885
886 /*
887  * afs_TruncateAllSegments
888  *
889  * Description:
890  *      Truncate a cache file.
891  *
892  * Parameters:
893  *      avc  : Ptr to vcache entry to truncate.
894  *      alen : Number of bytes to make the file.
895  *      areq : Ptr to request structure.
896  *
897  * Environment:
898  *      Called with avc write-locked; in VFS40 systems, pvnLock is also
899  *      held.
900  */
901 afs_TruncateAllSegments(avc, alen, areq, acred)
902     afs_size_t alen;
903     register struct vcache *avc;
904     struct vrequest *areq;
905     struct AFS_UCRED *acred;
906 { /*afs_TruncateAllSegments*/
907
908     register struct dcache *tdc;
909     register afs_int32 code;
910     register afs_int32 index;
911     afs_int32 newSize;
912
913     int dcCount, dcPos;
914     struct dcache **tdcArray;
915
916     AFS_STATCNT(afs_TruncateAllSegments);
917     avc->m.Date = osi_Time();
918     afs_Trace3(afs_iclSetp, CM_TRACE_TRUNCALL, ICL_TYPE_POINTER, avc,
919                         ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length), 
920                         ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(alen));
921     if (alen >= avc->m.Length) {
922         /*
923          * Special speedup since Sun's vm extends the file this way;
924          * we've never written to the file thus we can just set the new
925          * length and avoid the needless calls below.
926          * Also used for ftruncate calls which can extend the file.
927          * To completely minimize the possible extra StoreMini RPC, we really
928          * should keep the ExtendedPos as well and clear this flag if we
929          * truncate below that value before we store the file back.
930          */
931         avc->states |= CExtendedFile;
932         avc->m.Length = alen;
933         return 0;
934     }
935
936 #if     (defined(AFS_SUN5_ENV))
937
938     /* Zero unused portion of last page */
939     osi_VM_PreTruncate(avc, alen, acred);
940
941 #endif
942
943 #if     (defined(AFS_SUN5_ENV))
944     ObtainWriteLock(&avc->vlock, 546);
945     avc->activeV++;             /* Block new getpages */
946     ReleaseWriteLock(&avc->vlock);
947 #endif
948
949     ReleaseWriteLock(&avc->lock);
950     AFS_GUNLOCK();
951
952     /* Flush pages beyond end-of-file. */
953     osi_VM_Truncate(avc, alen, acred);
954
955     AFS_GLOCK();
956     ObtainWriteLock(&avc->lock,79);
957
958     avc->m.Length = alen;
959     
960     if (alen < avc->truncPos) avc->truncPos = alen;
961     code = DVHash(&avc->fid);
962
963     /* block out others from screwing with this table */
964     MObtainWriteLock(&afs_xdcache,287);
965
966     dcCount = 0;
967     for(index = afs_dvhashTbl[code]; index != NULLIDX;) {
968       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
969         tdc = afs_GetDSlot(index, 0);
970         ReleaseReadLock(&tdc->tlock);
971         if (!FidCmp(&tdc->f.fid, &avc->fid))
972             dcCount++;
973         afs_PutDCache(tdc);
974       }
975       index = afs_dvnextTbl[index];
976     }
977
978     /* Now allocate space where we can save those dcache entries, and
979      * do a second pass over them..  Since we're holding xdcache, it
980      * shouldn't be changing.
981      */
982     tdcArray = osi_Alloc(dcCount * sizeof(struct dcache *));
983     dcPos = 0;
984
985     for(index = afs_dvhashTbl[code]; index != NULLIDX;) {
986       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
987         tdc = afs_GetDSlot(index, 0);
988         ReleaseReadLock(&tdc->tlock);
989         if (!FidCmp(&tdc->f.fid, &avc->fid)) {
990             /* same file, and modified, we'll store it back */
991             if (dcPos < dcCount) {
992                 tdcArray[dcPos++] = tdc;
993             } else {
994                 afs_PutDCache(tdc);
995             }
996         } else {
997             afs_PutDCache(tdc);
998         }
999       }
1000       index = afs_dvnextTbl[index];
1001     }
1002
1003     MReleaseWriteLock(&afs_xdcache);
1004
1005     /* Now we loop over the array of dcache entries and truncate them */
1006     for (index = 0; index < dcPos; index++) {
1007         struct osi_file *tfile;
1008
1009         tdc = tdcArray[index];
1010
1011         newSize = alen - AFS_CHUNKTOBASE(tdc->f.chunk);
1012         if (newSize < 0) newSize = 0;
1013         ObtainSharedLock(&tdc->lock, 672);
1014         if (newSize < tdc->f.chunkBytes) {
1015             UpgradeSToWLock(&tdc->lock, 673);
1016             tfile = afs_CFileOpen(tdc->f.inode);
1017             afs_CFileTruncate(tfile, newSize);
1018             afs_CFileClose(tfile);
1019             afs_AdjustSize(tdc, newSize);
1020             ConvertWToSLock(&tdc->lock);
1021         }
1022         ReleaseSharedLock(&tdc->lock);
1023         afs_PutDCache(tdc);
1024     }
1025
1026     osi_Free(tdcArray, dcCount * sizeof(struct dcache *));
1027
1028 #if     (defined(AFS_SUN5_ENV))
1029     ObtainWriteLock(&avc->vlock, 547);
1030     if (--avc->activeV == 0 && (avc->vstates & VRevokeWait)) {
1031         avc->vstates &= ~VRevokeWait;
1032         afs_osi_Wakeup((char *)&avc->vstates);
1033     }
1034     ReleaseWriteLock(&avc->vlock);
1035 #endif
1036     return 0;
1037
1038 } /*afs_TruncateAllSegments*/
1039
1040