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