ObtainWriteLock(&avc->lock, 128);
avc->states |= CDirty;
- code = afs_TruncateAllSegments(avc, tsize, &treq, acred);
+ if (AFS_IS_DISCONNECTED && tsize >=avc->m.Length) {
+ /* If we're growing the file, and we're disconnected, we need
+ * to make the relevant dcache chunks appear ourselves. */
+ code = afs_ExtendSegments(avc, tsize, &treq);
+ } else {
+ code = afs_TruncateAllSegments(avc, tsize, &treq, acred);
+ }
#ifdef AFS_LINUX26_ENV
/* We must update the Linux kernel's idea of file size as soon as
* possible, to avoid racing with delayed writepages delivered by
* pdflush */
- if (code == 0)
+ if (code == 0)
i_size_write(AFSTOV(avc), tsize);
#endif
/* if date not explicitly set by this call, set it ourselves, since we
return code;
}
-
-
int
afs_MemWrite(register struct vcache *avc, struct uio *auio, int aio,
struct AFS_UCRED *acred, int noLock)
tvec = (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec));
#endif
while (totalLength > 0) {
- /*
- * The following line is necessary because afs_GetDCache with
- * flag == 4 expects the length field to be filled. It decides
- * from this whether it's necessary to fetch data into the chunk
- * before writing or not (when the whole chunk is overwritten!).
- */
- len = totalLength; /* write this amount by default */
- if (noLock) {
- tdc = afs_FindDCache(avc, filePos);
- if (tdc)
- ObtainWriteLock(&tdc->lock, 653);
- } else if (afs_blocksUsed >
- PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
- tdc = afs_FindDCache(avc, filePos);
- if (tdc) {
- ObtainWriteLock(&tdc->lock, 654);
- if (!hsame(tdc->f.versionNo, avc->m.DataVersion)
- || (tdc->dflags & DFFetching)) {
- ReleaseWriteLock(&tdc->lock);
- afs_PutDCache(tdc);
- tdc = NULL;
- }
- }
- if (!tdc) {
- afs_MaybeWakeupTruncateDaemon();
- while (afs_blocksUsed >
- PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
- ReleaseWriteLock(&avc->lock);
- if (afs_blocksUsed - afs_blocksDiscarded >
- PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
- afs_WaitForCacheDrain = 1;
- afs_osi_Sleep(&afs_WaitForCacheDrain);
- }
- afs_MaybeFreeDiscardedDCache();
- afs_MaybeWakeupTruncateDaemon();
- ObtainWriteLock(&avc->lock, 506);
- }
- avc->states |= CDirty;
- tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4);
- if (tdc)
- ObtainWriteLock(&tdc->lock, 655);
- }
- } else {
- tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4);
- if (tdc)
- ObtainWriteLock(&tdc->lock, 656);
- }
+ tdc = afs_ObtainDCacheForWriting(avc, filePos, totalLength, &treq,
+ noLock);
if (!tdc) {
error = EIO;
break;
}
- if (!(afs_indexFlags[tdc->index] & IFDataMod)) {
- afs_stats_cmperf.cacheCurrDirtyChunks++;
- afs_indexFlags[tdc->index] |= IFDataMod; /* so it doesn't disappear */
- }
- if (!(tdc->f.states & DWriting)) {
- /* don't mark entry as mod if we don't have to */
- tdc->f.states |= DWriting;
- tdc->dflags |= DFEntryMod;
- }
+
len = totalLength; /* write this amount by default */
offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk);
max = AFS_CHUNKTOSIZE(tdc->f.chunk); /* max size of this chunk */
tvec = (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec));
#endif
while (totalLength > 0) {
- /*
- * The following line is necessary because afs_GetDCache with
- * flag == 4 expects the length field to be filled. It decides
- * from this whether it's necessary to fetch data into the chunk
- * before writing or not (when the whole chunk is overwritten!).
- */
- len = totalLength; /* write this amount by default */
- /* read the cached info */
- if (noLock) {
- tdc = afs_FindDCache(avc, filePos);
- if (tdc)
- ObtainWriteLock(&tdc->lock, 657);
- } else if (afs_blocksUsed >
- PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
- tdc = afs_FindDCache(avc, filePos);
- if (tdc) {
- ObtainWriteLock(&tdc->lock, 658);
- if (!hsame(tdc->f.versionNo, avc->m.DataVersion)
- || (tdc->dflags & DFFetching)) {
- ReleaseWriteLock(&tdc->lock);
- afs_PutDCache(tdc);
- tdc = NULL;
- }
- }
- if (!tdc) {
- afs_MaybeWakeupTruncateDaemon();
- while (afs_blocksUsed >
- PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
- ReleaseWriteLock(&avc->lock);
- if (afs_blocksUsed - afs_blocksDiscarded >
- PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
- afs_WaitForCacheDrain = 1;
- afs_osi_Sleep(&afs_WaitForCacheDrain);
- }
- afs_MaybeFreeDiscardedDCache();
- afs_MaybeWakeupTruncateDaemon();
- ObtainWriteLock(&avc->lock, 509);
- }
- avc->states |= CDirty;
- tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4);
- if (tdc)
- ObtainWriteLock(&tdc->lock, 659);
- }
- } else {
- tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4);
- if (tdc)
- ObtainWriteLock(&tdc->lock, 660);
- }
+ tdc = afs_ObtainDCacheForWriting(avc, filePos, totalLength, &treq,
+ noLock);
if (!tdc) {
error = EIO;
break;
}
- if (!(afs_indexFlags[tdc->index] & IFDataMod)) {
- afs_stats_cmperf.cacheCurrDirtyChunks++;
- afs_indexFlags[tdc->index] |= IFDataMod; /* so it doesn't disappear */
- }
- if (!(tdc->f.states & DWriting)) {
- /* don't mark entry as mod if we don't have to */
- tdc->f.states |= DWriting;
- tdc->dflags |= DFEntryMod;
- }
#if defined(LINUX_USE_FH)
tfile = (struct osi_file *)osi_UFSOpen_fh(&tdc->f.fh, tdc->f.fh_type);
#else
}
+/*!
+ * Get a dcache ready for writing, respecting the current cache size limits
+ *
+ * len is required because afs_GetDCache with flag == 4 expects the length
+ * field to be filled. It decides from this whether it's necessary to fetch
+ * data into the chunk before writing or not (when the whole chunk is
+ * overwritten!).
+ *
+ * \param avc The vcache to fetch a dcache for
+ * \param filePos The start of the section to be written
+ * \param len The length of the section to be written
+ * \param areq
+ * \param noLock
+ *
+ * \return If successful, a reference counted dcache with tdc->lock held. Lock
+ * must be released and afs_PutDCache() called to free dcache.
+ * NULL on failure
+ *
+ * \note avc->lock must be held on entry. Function may release and reobtain
+ * avc->lock and GLOCK.
+ */
+
+struct dcache *
+afs_ObtainDCacheForWriting(struct vcache *avc, afs_size_t filePos,
+ afs_size_t len, struct vrequest *areq,
+ int noLock) {
+ struct dcache *tdc = NULL;
+ afs_size_t offset;
+
+ /* read the cached info */
+ if (noLock) {
+ tdc = afs_FindDCache(avc, filePos);
+ if (tdc)
+ ObtainWriteLock(&tdc->lock, 657);
+ } else if (afs_blocksUsed >
+ PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
+ tdc = afs_FindDCache(avc, filePos);
+ if (tdc) {
+ ObtainWriteLock(&tdc->lock, 658);
+ if (!hsame(tdc->f.versionNo, avc->m.DataVersion)
+ || (tdc->dflags & DFFetching)) {
+ ReleaseWriteLock(&tdc->lock);
+ afs_PutDCache(tdc);
+ tdc = NULL;
+ }
+ }
+ if (!tdc) {
+ afs_MaybeWakeupTruncateDaemon();
+ while (afs_blocksUsed >
+ PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
+ ReleaseWriteLock(&avc->lock);
+ if (afs_blocksUsed - afs_blocksDiscarded >
+ PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
+ afs_WaitForCacheDrain = 1;
+ afs_osi_Sleep(&afs_WaitForCacheDrain);
+ }
+ afs_MaybeFreeDiscardedDCache();
+ afs_MaybeWakeupTruncateDaemon();
+ ObtainWriteLock(&avc->lock, 509);
+ }
+ avc->states |= CDirty;
+ tdc = afs_GetDCache(avc, filePos, areq, &offset, &len, 4);
+ if (tdc)
+ ObtainWriteLock(&tdc->lock, 659);
+ }
+ } else {
+ tdc = afs_GetDCache(avc, filePos, areq, &offset, &len, 4);
+ if (tdc)
+ ObtainWriteLock(&tdc->lock, 660);
+ }
+ if (tdc) {
+ if (!(afs_indexFlags[tdc->index] & IFDataMod)) {
+ afs_stats_cmperf.cacheCurrDirtyChunks++;
+ afs_indexFlags[tdc->index] |= IFDataMod; /* so it doesn't disappear */
+ }
+ if (!(tdc->f.states & DWriting)) {
+ /* don't mark entry as mod if we don't have to */
+ tdc->f.states |= DWriting;
+ tdc->dflags |= DFEntryMod;
+ }
+ }
+ return tdc;
+}
+
#if defined(AFS_DISCON_ENV)
/*!
tname = afs_osi_Alloc(AFSNAMEMAX);
if (!tname) {
- printf("afs_ProcessOpCreate: Couldn't find file name\n");
+ printf("afs_ProcessOpCreate: Couldn't alloc space for file name\n");
return ENOMEM;
}
extern int afs_wakeup(register struct vcache *avc);
extern int afs_InitCacheFile(char *afile, ino_t ainode);
extern int afs_DCacheMissingChunks(struct vcache *avc);
+extern struct dcache *afs_ObtainDCacheForWriting(struct vcache *avc,
+ afs_size_t filePos,
+ afs_size_t len,
+ struct vrequest *areq,
+ int noLock);
+
/* afs_disconnected.c */
extern int afs_StoreAllSegments(register struct vcache *avc,
struct vrequest *areq, int sync);
extern int afs_InvalidateAllSegments(struct vcache *avc);
+extern int afs_ExtendSegments(struct vcache *avc,
+ afs_size_t alen, struct vrequest *areq);
extern int afs_TruncateAllSegments(register struct vcache *avc,
afs_size_t alen, struct vrequest *areq,
struct AFS_UCRED *acred);
return 0;
}
+/*!
+ *
+ * Extend a cache file
+ *
+ * \param avc pointer to vcache to extend data for
+ * \param alen Length to extend file to
+ * \param areq
+ *
+ * \note avc must be write locked. May release and reobtain avc and GLOCK
+ */
+int
+afs_ExtendSegments(struct vcache *avc, afs_size_t alen, struct vrequest *areq) {
+ afs_size_t offset, toAdd;
+ struct osi_file *tfile;
+ afs_int32 code = 0;
+ struct dcache *tdc;
+ void *zeros;
+
+ zeros = (void *) afs_osi_Alloc(AFS_PAGESIZE);
+ if (zeros == NULL)
+ return ENOMEM;
+ memset(zeros, 0, AFS_PAGESIZE);
+
+ while (avc->m.Length < alen) {
+ tdc = afs_ObtainDCacheForWriting(avc, avc->m.Length, alen - avc->m.Length, areq, 0);
+ if (!tdc) {
+ code = EIO;
+ break;
+ }
+
+ toAdd = alen - avc->m.Length;
+
+ offset = avc->m.Length - AFS_CHUNKTOBASE(tdc->f.chunk);
+ if (offset + toAdd > AFS_CHUNKTOSIZE(tdc->f.chunk)) {
+ toAdd = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset;
+ }
+#if defined(LINUX_USE_FH)
+ tfile = afs_CFileOpen(&tdc->f.fh, tdc->f.fh_type);
+#else
+ tfile = afs_CFileOpen(tdc->f.inode);
+#endif
+ while(tdc->validPos < avc->m.Length + toAdd) {
+ afs_size_t towrite;
+
+ towrite = (avc->m.Length + toAdd) - tdc->validPos;
+ if (towrite > AFS_PAGESIZE) towrite = AFS_PAGESIZE;
+
+ code = afs_CFileWrite(tfile,
+ tdc->validPos - AFS_CHUNKTOBASE(tdc->f.chunk),
+ zeros, towrite);
+ tdc->validPos += towrite;
+ }
+ afs_CFileClose(tfile);
+ afs_AdjustSize(tdc, offset + toAdd );
+ avc->m.Length += toAdd;
+ ReleaseWriteLock(&tdc->lock);
+ afs_PutDCache(tdc);
+ }
+
+ afs_osi_Free(zeros, AFS_PAGESIZE);
+ return code;
+}
/*
* afs_TruncateAllSegments