1da015db6a05e0fe0f25fb65b149fce5d5acb8e3
[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 static 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, code2;
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 #ifdef AFS_64BIT_CLIENT
62           retry:
63 #endif
64             RX_AFS_GUNLOCK();
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->f.m.Date;
77             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STOREDATA);
78             afs_Trace4(afs_iclSetp, CM_TRACE_STOREDATA64, ICL_TYPE_FID,
79                        &avc->f.fid.Fid, ICL_TYPE_OFFSET,
80                        ICL_HANDLE_OFFSET(avc->f.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->f.fid.Fid,
89                                            &InStatus, avc->f.m.Length,
90                                            (afs_size_t) 0, tlen);
91             } else {
92                 afs_int32 l1, l2;
93                 l1 = avc->f.m.Length;
94                 l2 = tlen;
95                 if ((avc->f.m.Length > 0x7fffffff) ||
96                     (tlen > 0x7fffffff) ||
97                     ((0x7fffffff - tlen) < avc->f.m.Length))
98                     return EFBIG;
99                 code =
100                     StartRXAFS_StoreData(tcall,
101                                          (struct AFSFid *)&avc->f.fid.Fid,
102                                          &InStatus, l1, 0, l2);
103             }
104 #else /* AFS_64BIT_CLIENT */
105             code =
106                 StartRXAFS_StoreData(tcall, (struct AFSFid *)&avc->f.fid.Fid,
107                                      &InStatus, avc->f.m.Length, 0, tlen);
108 #endif /* AFS_64BIT_CLIENT */
109             if (code == 0) {
110                 code = EndRXAFS_StoreData(tcall, &OutStatus, &tsync);
111             }
112             code2 = rx_EndCall(tcall, code);
113             if (code2 && !code)
114                 code = code2;
115             RX_AFS_GLOCK();
116             XSTATS_END_TIME;
117 #ifdef AFS_64BIT_CLIENT
118             if (code == RXGEN_OPCODE && !afs_serverHasNo64Bit(tc)) {
119                 afs_serverSetNo64Bit(tc);
120                 goto retry;
121             }
122 #endif /* AFS_64BIT_CLIENT */
123         } else
124             code = -1;
125     } while (afs_Analyze
126              (tc, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_STOREDATA,
127               SHARED_LOCK, NULL));
128
129     if (code == 0)
130         afs_ProcessFS(avc, &OutStatus, areq);
131
132     return code;
133 }                               /*afs_StoreMini */
134
135 /*
136  * afs_StoreAllSegments
137  *
138  * Description:
139  *      Stores all modified segments back to server
140  *
141  * Parameters:
142  *      avc  : Pointer to vcache entry.
143  *      areq : Pointer to request structure.
144  *
145  * Environment:
146  *      Called with avc write-locked.
147  */
148 #if defined (AFS_HPUX_ENV)
149 int NCHUNKSATONCE = 3;
150 #else
151 int NCHUNKSATONCE = 64;
152 #endif
153 int afs_dvhack = 0;
154
155
156 int
157 afs_StoreAllSegments(register struct vcache *avc, struct vrequest *areq,
158                      int sync)
159 {
160     register struct dcache *tdc;
161     register afs_int32 code = 0;
162     register afs_int32 index;
163     register afs_int32 origCBs, foreign = 0;
164     int hash;
165     afs_hyper_t newDV, oldDV;   /* DV when we start, and finish, respectively */
166     struct dcache **dcList;
167     unsigned int i, j, minj, moredata, high, off;
168     afs_size_t tlen;
169     afs_size_t maxStoredLength; /* highest offset we've written to server. */
170     int safety;
171
172     AFS_STATCNT(afs_StoreAllSegments);
173
174     hset(oldDV, avc->f.m.DataVersion);
175     hset(newDV, avc->f.m.DataVersion);
176     hash = DVHash(&avc->f.fid);
177     foreign = (avc->f.states & CForeign);
178     dcList = (struct dcache **)osi_AllocLargeSpace(AFS_LRALLOCSIZ);
179     afs_Trace2(afs_iclSetp, CM_TRACE_STOREALL, ICL_TYPE_POINTER, avc,
180                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length));
181 #if !defined(AFS_AIX32_ENV) && !defined(AFS_SGI65_ENV)
182     /* In the aix vm implementation we need to do the vm_writep even
183      * on the memcache case since that's we adjust the file's size
184      * and finish flushing partial vm pages.
185      */
186     if ((cacheDiskType != AFS_FCACHE_TYPE_MEM) || (sync & AFS_LASTSTORE))
187 #endif /* !AFS_AIX32_ENV && !AFS_SGI65_ENV */
188     {
189         /* If we're not diskless, reading a file may stress the VM
190          * system enough to cause a pageout, and this vnode would be
191          * locked when the pageout occurs.  We can prevent this problem
192          * by making sure all dirty pages are already flushed.  We don't
193          * do this when diskless because reading a diskless (i.e.
194          * memory-resident) chunk doesn't require using new VM, and we
195          * also don't want to dump more dirty data into a diskless cache,
196          * since they're smaller, and we might exceed its available
197          * space.
198          */
199 #if     defined(AFS_SUN5_ENV)
200         if (sync & AFS_VMSYNC_INVAL)    /* invalidate VM pages */
201             osi_VM_TryToSmush(avc, CRED(), 1);
202         else
203 #endif
204             osi_VM_StoreAllSegments(avc);
205     }
206     if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) {
207         /* This will probably make someone sad ... */
208         /*printf("Net down in afs_StoreSegments\n");*/
209         return ENETDOWN;
210     }
211     ConvertWToSLock(&avc->lock);
212
213     /*
214      * Subsequent code expects a sorted list, and it expects all the
215      * chunks in the list to be contiguous, so we need a sort and a
216      * while loop in here, too - but this will work for a first pass...
217      * 92.10.05 - OK, there's a sort in here now.  It's kind of a modified
218      *            bin sort, I guess.  Chunk numbers start with 0
219      *
220      * - Have to get a write lock on xdcache because GetDSlot might need it (if
221      *   the chunk doesn't have a dcache struct).
222      *   This seems like overkill in most cases.
223      * - I'm not sure that it's safe to do "index = .hvNextp", then unlock 
224      *   xdcache, then relock xdcache and try to use index.  It is done
225      *   a lot elsewhere in the CM, but I'm not buying that argument.
226      * - should be able to check IFDataMod without doing the GetDSlot (just
227      *   hold afs_xdcache).  That way, it's easy to do this without the
228      *   writelock on afs_xdcache, and we save unneccessary disk
229      *   operations. I don't think that works, 'cuz the next pointers 
230      *   are still on disk.
231      */
232     origCBs = afs_allCBs;
233
234     maxStoredLength = 0;
235     tlen = avc->f.m.Length;
236     minj = 0;
237
238     do {
239         memset(dcList, 0, NCHUNKSATONCE * sizeof(struct dcache *));
240         high = 0;
241         moredata = FALSE;
242
243         /* lock and start over from beginning of hash chain 
244          * in order to avoid a race condition. */
245         ObtainWriteLock(&afs_xdcache, 284);
246         index = afs_dvhashTbl[hash];
247
248         for (j = 0; index != NULLIDX;) {
249             if ((afs_indexFlags[index] & IFDataMod)
250                 && (afs_indexUnique[index] == avc->f.fid.Fid.Unique)) {
251                 tdc = afs_GetDSlot(index, 0);   /* refcount+1. */
252                 ReleaseReadLock(&tdc->tlock);
253                 if (!FidCmp(&tdc->f.fid, &avc->f.fid) && tdc->f.chunk >= minj) {
254                     off = tdc->f.chunk - minj;
255                     if (off < NCHUNKSATONCE) {
256                         if (dcList[off])
257                             osi_Panic("dclist slot already in use!");
258                         dcList[off] = tdc;
259                         if (off > high)
260                             high = off;
261                         j++;
262                         /* DCLOCKXXX: chunkBytes is protected by tdc->lock which we
263                          * can't grab here, due to lock ordering with afs_xdcache.
264                          * So, disable this shortcut for now.  -- kolya 2001-10-13
265                          */
266                         /* shortcut: big win for little files */
267                         /* tlen -= tdc->f.chunkBytes;
268                          * if (tlen <= 0)
269                          *    break;
270                          */
271                     } else {
272                         moredata = TRUE;
273                         afs_PutDCache(tdc);
274                         if (j == NCHUNKSATONCE)
275                             break;
276                     }
277                 } else {
278                     afs_PutDCache(tdc);
279                 }
280             }
281             index = afs_dvnextTbl[index];
282         }
283         ReleaseWriteLock(&afs_xdcache);
284
285         /* this guy writes chunks, puts back dcache structs, and bumps newDV */
286         /* "moredata" just says "there are more dirty chunks yet to come".
287          */
288         if (j) {
289             code =
290                 afs_CacheStoreVCache(dcList, avc, areq, sync,
291                                    minj, high, moredata,
292                                    &newDV, &maxStoredLength);
293             /* Release any zero-length dcache entries in our interval
294              * that we locked but didn't store back above.
295              */
296             for (j = 0; j <= high; j++) {
297                 tdc = dcList[j];
298                 if (tdc) {
299                     osi_Assert(tdc->f.chunkBytes == 0);
300                     ReleaseSharedLock(&tdc->lock);
301                     afs_PutDCache(tdc);
302                 }
303             }
304         }
305         /* if (j) */
306         minj += NCHUNKSATONCE;
307     } while (!code && moredata);
308
309     UpgradeSToWLock(&avc->lock, 29);
310
311     /* send a trivial truncation store if did nothing else */
312     if (code == 0) {
313         /*
314          * Call StoreMini if we haven't written enough data to extend the
315          * file at the fileserver to the client's notion of the file length.
316          */
317         if ((avc->f.truncPos != AFS_NOTRUNC) 
318             || ((avc->f.states & CExtendedFile)
319                 && (maxStoredLength < avc->f.m.Length))) {
320             code = afs_StoreMini(avc, areq);
321             if (code == 0)
322                 hadd32(newDV, 1);       /* just bumped here, too */
323         }
324         avc->f.states &= ~CExtendedFile;
325     }
326
327     /*
328      * Finally, turn off DWriting, turn on DFEntryMod,
329      * update f.versionNo.
330      * A lot of this could be integrated into the loop above 
331      */
332     if (!code) {
333         afs_hyper_t h_unset;
334         hones(h_unset);
335
336         minj = 0;
337
338         do {
339             moredata = FALSE;
340             memset(dcList, 0,
341                    NCHUNKSATONCE * sizeof(struct dcache *));
342
343             /* overkill, but it gets the lock in case GetDSlot needs it */
344             ObtainWriteLock(&afs_xdcache, 285);
345
346             for (j = 0, safety = 0, index = afs_dvhashTbl[hash];
347                  index != NULLIDX && safety < afs_cacheFiles + 2;) {
348
349                 if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
350                     tdc = afs_GetDSlot(index, 0);
351                     ReleaseReadLock(&tdc->tlock);
352
353                     if (!FidCmp(&tdc->f.fid, &avc->f.fid)
354                         && tdc->f.chunk >= minj) {
355                         off = tdc->f.chunk - minj;
356                         if (off < NCHUNKSATONCE) {
357                             /* this is the file, and the correct chunk range */
358                             if (j >= NCHUNKSATONCE)
359                                 osi_Panic
360                                     ("Too many dcache entries in range\n");
361                             dcList[j++] = tdc;
362                         } else {
363                             moredata = TRUE;
364                             afs_PutDCache(tdc);
365                             if (j == NCHUNKSATONCE)
366                                 break;
367                         }
368                     } else {
369                         afs_PutDCache(tdc);
370                     }
371                 }
372
373                 index = afs_dvnextTbl[index];
374             }
375             ReleaseWriteLock(&afs_xdcache);
376
377             for (i = 0; i < j; i++) {
378                 /* Iterate over the dcache entries we collected above */
379                 tdc = dcList[i];
380                 ObtainSharedLock(&tdc->lock, 677);
381
382                 /* was code here to clear IFDataMod, but it should only be done
383                  * in storedcache and storealldcache.
384                  */
385                 /* Only increase DV if we had up-to-date data to start with.
386                  * Otherwise, we could be falsely upgrading an old chunk
387                  * (that we never read) into one labelled with the current
388                  * DV #.  Also note that we check that no intervening stores
389                  * occurred, otherwise we might mislabel cache information
390                  * for a chunk that we didn't store this time
391                  */
392                 /* Don't update the version number if it's not yet set. */
393                 if (!hsame(tdc->f.versionNo, h_unset)
394                     && hcmp(tdc->f.versionNo, oldDV) >= 0) {
395
396                     if ((!(afs_dvhack || foreign)
397                          && hsame(avc->f.m.DataVersion, newDV))
398                         || ((afs_dvhack || foreign)
399                             && (origCBs == afs_allCBs))) {
400                         /* no error, this is the DV */
401
402                         UpgradeSToWLock(&tdc->lock, 678);
403                         hset(tdc->f.versionNo, avc->f.m.DataVersion);
404                         tdc->dflags |= DFEntryMod;
405                         ConvertWToSLock(&tdc->lock);
406                     }
407                 }
408
409                 ReleaseSharedLock(&tdc->lock);
410                 afs_PutDCache(tdc);
411             }
412
413             minj += NCHUNKSATONCE;
414
415         } while (moredata);
416     }
417
418     if (code) {
419         /*
420          * Invalidate chunks after an error for ccores files since
421          * afs_inactive won't be called for these and they won't be
422          * invalidated. Also discard data if it's a permanent error from the
423          * fileserver.
424          */
425         if (areq->permWriteError || (avc->f.states & (CCore1 | CCore))) {
426             afs_InvalidateAllSegments(avc);
427         }
428     }
429     afs_Trace3(afs_iclSetp, CM_TRACE_STOREALLDONE, ICL_TYPE_POINTER, avc,
430                ICL_TYPE_INT32, avc->f.m.Length, ICL_TYPE_INT32, code);
431     /* would like a Trace5, but it doesn't exist... */
432     afs_Trace3(afs_iclSetp, CM_TRACE_AVCLOCKER, ICL_TYPE_POINTER, avc,
433                ICL_TYPE_INT32, avc->lock.wait_states, ICL_TYPE_INT32,
434                avc->lock.excl_locked);
435     afs_Trace4(afs_iclSetp, CM_TRACE_AVCLOCKEE, ICL_TYPE_POINTER, avc,
436                ICL_TYPE_INT32, avc->lock.wait_states, ICL_TYPE_INT32,
437                avc->lock.readers_reading, ICL_TYPE_INT32,
438                avc->lock.num_waiting);
439
440     /*
441      * Finally, if updated DataVersion matches newDV, we did all of the
442      * stores.  If mapDV indicates that the page cache was flushed up
443      * to when we started the store, then we can relabel them as flushed
444      * as recently as newDV.
445      * Turn off CDirty bit because the stored data is now in sync with server.
446      */
447     if (code == 0 && hcmp(avc->mapDV, oldDV) >= 0) {
448         if ((!(afs_dvhack || foreign) && hsame(avc->f.m.DataVersion, newDV))
449             || ((afs_dvhack || foreign) && (origCBs == afs_allCBs))) {
450             hset(avc->mapDV, newDV);
451             avc->f.states &= ~CDirty;
452         }
453     }
454     osi_FreeLargeSpace(dcList);
455
456     /* If not the final write a temporary error is ok. */
457     if (code && !areq->permWriteError && !(sync & AFS_LASTSTORE))
458         code = 0;
459
460     return code;
461
462 }                               /*afs_StoreAllSegments (new 03/02/94) */
463
464
465 /*
466  * afs_InvalidateAllSegments
467  *
468  * Description:
469  *      Invalidates all chunks for a given file
470  *
471  * Parameters:
472  *      avc      : Pointer to vcache entry.
473  *
474  * Environment:
475  *      For example, called after an error has been detected.  Called
476  *      with avc write-locked, and afs_xdcache unheld.
477  */
478
479 int
480 afs_InvalidateAllSegments(struct vcache *avc)
481 {
482     struct dcache *tdc;
483     afs_int32 hash;
484     afs_int32 index;
485     struct dcache **dcList;
486     int i, dcListMax, dcListCount;
487
488     AFS_STATCNT(afs_InvalidateAllSegments);
489     afs_Trace2(afs_iclSetp, CM_TRACE_INVALL, ICL_TYPE_POINTER, avc,
490                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length));
491     hash = DVHash(&avc->f.fid);
492     avc->f.truncPos = AFS_NOTRUNC;      /* don't truncate later */
493     avc->f.states &= ~CExtendedFile;    /* not any more */
494     ObtainWriteLock(&afs_xcbhash, 459);
495     afs_DequeueCallback(avc);
496     avc->f.states &= ~(CStatd | CDirty);        /* mark status information as bad, too */
497     ReleaseWriteLock(&afs_xcbhash);
498     if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
499         osi_dnlc_purgedp(avc);
500     /* Blow away pages; for now, only for Solaris */
501 #if     (defined(AFS_SUN5_ENV))
502     if (WriteLocked(&avc->lock))
503         osi_ReleaseVM(avc, (afs_ucred_t *)0);
504 #endif
505     /*
506      * Block out others from screwing with this table; is a read lock
507      * sufficient?
508      */
509     ObtainWriteLock(&afs_xdcache, 286);
510     dcListMax = 0;
511
512     for (index = afs_dvhashTbl[hash]; index != NULLIDX;) {
513         if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
514             tdc = afs_GetDSlot(index, 0);
515             ReleaseReadLock(&tdc->tlock);
516             if (!FidCmp(&tdc->f.fid, &avc->f.fid))
517                 dcListMax++;
518             afs_PutDCache(tdc);
519         }
520         index = afs_dvnextTbl[index];
521     }
522
523     dcList = osi_Alloc(dcListMax * sizeof(struct dcache *));
524     dcListCount = 0;
525
526     for (index = afs_dvhashTbl[hash]; index != NULLIDX;) {
527         if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
528             tdc = afs_GetDSlot(index, 0);
529             ReleaseReadLock(&tdc->tlock);
530             if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
531                 /* same file? we'll zap it */
532                 if (afs_indexFlags[index] & IFDataMod) {
533                     afs_stats_cmperf.cacheCurrDirtyChunks--;
534                     /* don't write it back */
535                     afs_indexFlags[index] &= ~IFDataMod;
536                 }
537                 afs_indexFlags[index] &= ~IFAnyPages;
538                 if (dcListCount < dcListMax)
539                     dcList[dcListCount++] = tdc;
540                 else
541                     afs_PutDCache(tdc);
542             } else {
543                 afs_PutDCache(tdc);
544             }
545         }
546         index = afs_dvnextTbl[index];
547     }
548     ReleaseWriteLock(&afs_xdcache);
549
550     for (i = 0; i < dcListCount; i++) {
551         tdc = dcList[i];
552
553         ObtainWriteLock(&tdc->lock, 679);
554         ZapDCE(tdc);
555         if (vType(avc) == VDIR)
556             DZap(tdc);
557         ReleaseWriteLock(&tdc->lock);
558         afs_PutDCache(tdc);
559     }
560
561     osi_Free(dcList, dcListMax * sizeof(struct dcache *));
562
563     return 0;
564 }
565
566 /*! 
567  * 
568  * Extend a cache file
569  *
570  * \param avc pointer to vcache to extend data for
571  * \param alen Length to extend file to
572  * \param areq
573  *
574  * \note avc must be write locked. May release and reobtain avc and GLOCK
575  */
576 int
577 afs_ExtendSegments(struct vcache *avc, afs_size_t alen, struct vrequest *areq) {
578     afs_size_t offset, toAdd;
579     struct osi_file *tfile;
580     afs_int32 code = 0;
581     struct dcache *tdc;
582     void *zeros;
583
584     zeros = (void *) afs_osi_Alloc(AFS_PAGESIZE);
585     if (zeros == NULL)
586         return ENOMEM;
587     memset(zeros, 0, AFS_PAGESIZE);
588
589     while (avc->f.m.Length < alen) {
590         tdc = afs_ObtainDCacheForWriting(avc, avc->f.m.Length, alen - avc->f.m.Length, areq, 0);
591         if (!tdc) {
592             code = EIO;
593             break;
594         }
595
596         toAdd = alen - avc->f.m.Length;
597
598         offset = avc->f.m.Length - AFS_CHUNKTOBASE(tdc->f.chunk);
599         if (offset + toAdd > AFS_CHUNKTOSIZE(tdc->f.chunk)) {
600             toAdd = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset;
601         }
602         tfile = afs_CFileOpen(&tdc->f.inode);
603         while(tdc->validPos < avc->f.m.Length + toAdd) {
604              afs_size_t towrite;
605
606              towrite = (avc->f.m.Length + toAdd) - tdc->validPos;
607              if (towrite > AFS_PAGESIZE) towrite = AFS_PAGESIZE;
608
609              code = afs_CFileWrite(tfile, 
610                                    tdc->validPos - AFS_CHUNKTOBASE(tdc->f.chunk), 
611                                    zeros, towrite);
612              tdc->validPos += towrite;
613         }
614         afs_CFileClose(tfile);
615         afs_AdjustSize(tdc, offset + toAdd );
616         avc->f.m.Length += toAdd;
617         ReleaseWriteLock(&tdc->lock);
618         afs_PutDCache(tdc);
619     }
620
621     afs_osi_Free(zeros, AFS_PAGESIZE);
622     return code;
623 }
624
625 /*
626  * afs_TruncateAllSegments
627  *
628  * Description:
629  *      Truncate a cache file.
630  *
631  * Parameters:
632  *      avc  : Ptr to vcache entry to truncate.
633  *      alen : Number of bytes to make the file.
634  *      areq : Ptr to request structure.
635  *
636  * Environment:
637  *      Called with avc write-locked; in VFS40 systems, pvnLock is also
638  *      held.
639  */
640 int
641 afs_TruncateAllSegments(register struct vcache *avc, afs_size_t alen,
642                         struct vrequest *areq, afs_ucred_t *acred)
643 {
644     register struct dcache *tdc;
645     register afs_int32 code;
646     register afs_int32 index;
647     afs_size_t newSize;
648
649     int dcCount, dcPos;
650     struct dcache **tdcArray;
651
652     AFS_STATCNT(afs_TruncateAllSegments);
653     avc->f.m.Date = osi_Time();
654     afs_Trace3(afs_iclSetp, CM_TRACE_TRUNCALL, ICL_TYPE_POINTER, avc,
655                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length),
656                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(alen));
657     if (alen >= avc->f.m.Length) {
658         /*
659          * Special speedup since Sun's vm extends the file this way;
660          * we've never written to the file thus we can just set the new
661          * length and avoid the needless calls below.
662          * Also used for ftruncate calls which can extend the file.
663          * To completely minimize the possible extra StoreMini RPC, we really
664          * should keep the ExtendedPos as well and clear this flag if we
665          * truncate below that value before we store the file back.
666          */
667         avc->f.states |= CExtendedFile;
668         avc->f.m.Length = alen;
669         return 0;
670     }
671 #if     (defined(AFS_SUN5_ENV))
672
673     /* Zero unused portion of last page */
674     osi_VM_PreTruncate(avc, alen, acred);
675
676 #endif
677
678 #if     (defined(AFS_SUN5_ENV))
679     ObtainWriteLock(&avc->vlock, 546);
680     avc->activeV++;             /* Block new getpages */
681     ReleaseWriteLock(&avc->vlock);
682 #endif
683
684     ReleaseWriteLock(&avc->lock);
685     AFS_GUNLOCK();
686
687     /* Flush pages beyond end-of-file. */
688     osi_VM_Truncate(avc, alen, acred);
689
690     AFS_GLOCK();
691     ObtainWriteLock(&avc->lock, 79);
692
693     avc->f.m.Length = alen;
694
695     if (alen < avc->f.truncPos)
696         avc->f.truncPos = alen;
697     code = DVHash(&avc->f.fid);
698
699     /* block out others from screwing with this table */
700     ObtainWriteLock(&afs_xdcache, 287);
701
702     dcCount = 0;
703     for (index = afs_dvhashTbl[code]; index != NULLIDX;) {
704         if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
705             tdc = afs_GetDSlot(index, 0);
706             ReleaseReadLock(&tdc->tlock);
707             if (!FidCmp(&tdc->f.fid, &avc->f.fid))
708                 dcCount++;
709             afs_PutDCache(tdc);
710         }
711         index = afs_dvnextTbl[index];
712     }
713
714     /* Now allocate space where we can save those dcache entries, and
715      * do a second pass over them..  Since we're holding xdcache, it
716      * shouldn't be changing.
717      */
718     tdcArray = osi_Alloc(dcCount * sizeof(struct dcache *));
719     dcPos = 0;
720
721     for (index = afs_dvhashTbl[code]; index != NULLIDX;) {
722         if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
723             tdc = afs_GetDSlot(index, 0);
724             ReleaseReadLock(&tdc->tlock);
725             if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
726                 /* same file, and modified, we'll store it back */
727                 if (dcPos < dcCount) {
728                     tdcArray[dcPos++] = tdc;
729                 } else {
730                     afs_PutDCache(tdc);
731                 }
732             } else {
733                 afs_PutDCache(tdc);
734             }
735         }
736         index = afs_dvnextTbl[index];
737     }
738
739     ReleaseWriteLock(&afs_xdcache);
740
741     /* Now we loop over the array of dcache entries and truncate them */
742     for (index = 0; index < dcPos; index++) {
743         struct osi_file *tfile;
744
745         tdc = tdcArray[index];
746
747         newSize = alen - AFS_CHUNKTOBASE(tdc->f.chunk);
748         if (newSize < 0)
749             newSize = 0;
750         ObtainSharedLock(&tdc->lock, 672);
751         if (newSize < tdc->f.chunkBytes && newSize < MAX_AFS_UINT32) {
752             UpgradeSToWLock(&tdc->lock, 673);
753             tfile = afs_CFileOpen(&tdc->f.inode);
754             afs_CFileTruncate(tfile, (afs_int32)newSize);
755             afs_CFileClose(tfile);
756             afs_AdjustSize(tdc, (afs_int32)newSize);
757             if (alen < tdc->validPos) {
758                 if (alen < AFS_CHUNKTOBASE(tdc->f.chunk))
759                     tdc->validPos = 0;
760                 else
761                     tdc->validPos = alen;
762             }
763             ConvertWToSLock(&tdc->lock);
764         }
765         ReleaseSharedLock(&tdc->lock);
766         afs_PutDCache(tdc);
767     }
768
769     osi_Free(tdcArray, dcCount * sizeof(struct dcache *));
770
771 #if     (defined(AFS_SUN5_ENV))
772     ObtainWriteLock(&avc->vlock, 547);
773     if (--avc->activeV == 0 && (avc->vstates & VRevokeWait)) {
774         avc->vstates &= ~VRevokeWait;
775         afs_osi_Wakeup((char *)&avc->vstates);
776     }
777     ReleaseWriteLock(&avc->vlock);
778 #endif
779
780     return 0;
781 }