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