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