2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include "afs/param.h"
15 #include "afs/sysincludes.h" /* Standard vendor system headers */
16 #ifndef AFS_LINUX22_ENV
17 #include "rpc/types.h"
25 #endif /* AFS_ALPHA_ENV */
26 #include "afsincludes.h" /* Afs-based standard headers */
27 #include "afs/afs_stats.h" /* statistics */
29 /* memory cache routines */
30 static struct memCacheEntry *memCache;
31 static int memCacheBlkSize = 8192;
32 static int memMaxBlkNumber = 0;
33 static int memAllocMaySleep = 0;
35 extern int cacheDiskType;
37 int afs_InitMemCache(int blkCount, int blkSize, int flags)
41 AFS_STATCNT(afs_InitMemCache);
43 memCacheBlkSize = blkSize;
45 memMaxBlkNumber = blkCount;
46 memCache = (struct memCacheEntry *)
47 afs_osi_Alloc(memMaxBlkNumber * sizeof(struct memCacheEntry));
48 if (flags & AFSCALL_INIT_MEMCACHE_SLEEP) {
52 for(index = 0; index < memMaxBlkNumber; index++) {
54 (memCache+index)->size = 0;
55 (memCache+index)->dataSize = memCacheBlkSize;
56 LOCK_INIT(&((memCache+index)->afs_memLock), "afs_memLock");
57 if (memAllocMaySleep) {
58 blk = afs_osi_Alloc(memCacheBlkSize);
60 blk = afs_osi_Alloc_NoSleep(memCacheBlkSize);
64 (memCache+index)->data = blk;
65 memset((memCache+index)->data, 0, memCacheBlkSize);
67 #if defined(AFS_SGI62_ENV) || defined(AFS_HAVE_VXFS)
68 afs_InitDualFSCacheOps((struct vnode*)0);
74 printf("afsd: memCache allocation failure at %d KB.\n",
75 (index * memCacheBlkSize) / 1024);
77 afs_osi_Free((memCache+index)->data, memCacheBlkSize);
78 (memCache+index)->data = NULL;
84 int afs_MemCacheClose(char *file)
89 void *afs_MemCacheOpen(ino_t blkno)
91 struct memCacheEntry *mep;
93 if (blkno < 0 || blkno > memMaxBlkNumber) {
94 osi_Panic("afs_MemCacheOpen: invalid block #");
96 mep = (memCache + blkno);
97 afs_Trace4(afs_iclSetp, CM_TRACE_MEMOPEN,
98 ICL_TYPE_INT32, blkno,
99 ICL_TYPE_POINTER, mep,
100 ICL_TYPE_POINTER, mep->data,
101 ICL_TYPE_STRING, mep->data);
106 * this routine simulates a read in the Memory Cache
108 int afs_MemReadBlk(register struct memCacheEntry *mceP, int offset, char *dest, int size)
112 MObtainReadLock(&mceP->afs_memLock);
113 AFS_STATCNT(afs_MemReadBlk);
115 MReleaseReadLock(&mceP->afs_memLock);
118 /* use min of bytes in buffer or requested size */
119 bytesRead = (size < mceP->size - offset) ? size :
124 memcpy(dest, mceP->data + offset, bytesRead);
130 MReleaseReadLock(&mceP->afs_memLock);
135 * this routine simulates a readv in the Memory Cache
137 int afs_MemReadvBlk(register struct memCacheEntry *mceP, int offset, struct iovec *iov, int nio, int size)
143 MObtainReadLock(&mceP->afs_memLock);
144 AFS_STATCNT(afs_MemReadBlk);
146 MReleaseReadLock(&mceP->afs_memLock);
149 /* use min of bytes in buffer or requested size */
150 bytesRead = (size < mceP->size - offset) ? size :
154 for (i = 0 , size = bytesRead ; i < nio && size > 0 ; i++) {
155 bytesToRead = (size < iov[i].iov_len) ? size : iov[i].iov_len;
157 memcpy(iov[i].iov_base, mceP->data + offset, bytesToRead);
159 offset += bytesToRead;
166 MReleaseReadLock(&mceP->afs_memLock);
170 int afs_MemReadUIO(ino_t blkno, struct uio *uioP)
172 register struct memCacheEntry *mceP = (struct memCacheEntry *)afs_MemCacheOpen(blkno);
173 int length = mceP->size - uioP->uio_offset;
176 AFS_STATCNT(afs_MemReadUIO);
177 MObtainReadLock(&mceP->afs_memLock);
178 length = (length < uioP->uio_resid) ? length : uioP->uio_resid;
179 AFS_UIOMOVE(mceP->data + uioP->uio_offset, length, UIO_READ, uioP, code);
180 MReleaseReadLock(&mceP->afs_memLock);
184 /*XXX: this extends a block arbitrarily to support big directories */
185 int afs_MemWriteBlk(register struct memCacheEntry *mceP, int offset, char *src, int size)
187 AFS_STATCNT(afs_MemWriteBlk);
188 MObtainWriteLock(&mceP->afs_memLock,560);
189 if (size + offset > mceP->dataSize) {
190 char *oldData = mceP->data;
192 if (memAllocMaySleep) {
193 mceP->data = afs_osi_Alloc(size+offset);
195 mceP->data = afs_osi_Alloc_NoSleep(size+offset);
197 if ( mceP->data == NULL ) /* no available memory */
199 mceP->data = oldData; /* revert back change that was made */
200 MReleaseWriteLock(&mceP->afs_memLock);
201 afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
206 /* may overlap, but this is OK */
208 memcpy(mceP->data, oldData, mceP->size);
210 afs_osi_Free(oldData,mceP->dataSize);
211 mceP->dataSize = size+offset;
214 if (mceP->size < offset)
215 memset(mceP->data+mceP->size, 0, offset-mceP->size);
216 memcpy(mceP->data + offset, src, size);
218 mceP->size = (size+offset < mceP->size) ? mceP->size :
221 MReleaseWriteLock(&mceP->afs_memLock);
225 /*XXX: this extends a block arbitrarily to support big directories */
226 int afs_MemWritevBlk(register struct memCacheEntry *mceP, int offset, struct iovec *iov, int nio, int size)
231 AFS_STATCNT(afs_MemWriteBlk);
232 MObtainWriteLock(&mceP->afs_memLock,561);
233 if (offset + size > mceP->dataSize) {
234 char *oldData = mceP->data;
236 mceP->data = afs_osi_Alloc(size+offset);
238 /* may overlap, but this is OK */
240 memcpy(mceP->data, oldData, mceP->size);
242 afs_osi_Free(oldData,mceP->dataSize);
243 mceP->dataSize = size+offset;
246 if (mceP->size < offset)
247 memset(mceP->data+mceP->size, 0, offset-mceP->size);
248 for (bytesWritten = 0, i = 0 ; i < nio && size > 0 ; i++) {
249 bytesToWrite = (size < iov[i].iov_len) ? size : iov[i].iov_len;
250 memcpy(mceP->data + offset, iov[i].iov_base, bytesToWrite);
251 offset += bytesToWrite;
252 bytesWritten += bytesToWrite;
253 size -= bytesToWrite;
255 mceP->size = (offset < mceP->size) ? mceP->size : offset;
258 MReleaseWriteLock(&mceP->afs_memLock);
262 int afs_MemWriteUIO(ino_t blkno, struct uio *uioP)
264 register struct memCacheEntry *mceP = (struct memCacheEntry *)afs_MemCacheOpen(blkno);
267 AFS_STATCNT(afs_MemWriteUIO);
268 MObtainWriteLock(&mceP->afs_memLock,312);
269 if(uioP->uio_resid + uioP->uio_offset > mceP->dataSize) {
270 char *oldData = mceP->data;
272 mceP->data = afs_osi_Alloc(uioP->uio_resid + uioP->uio_offset);
275 memcpy(mceP->data, oldData, mceP->size);
278 afs_osi_Free(oldData,mceP->dataSize);
279 mceP->dataSize = uioP->uio_resid + uioP->uio_offset;
281 if (mceP->size < uioP->uio_offset)
282 memset(mceP->data+mceP->size, 0, (int)(uioP->uio_offset-mceP->size));
283 AFS_UIOMOVE(mceP->data+uioP->uio_offset, uioP->uio_resid, UIO_WRITE, uioP, code);
284 if (uioP->uio_offset > mceP->size)
285 mceP->size = uioP->uio_offset;
287 MReleaseWriteLock(&mceP->afs_memLock);
291 int afs_MemCacheTruncate(register struct memCacheEntry *mceP, int size)
293 AFS_STATCNT(afs_MemCacheTruncate);
295 MObtainWriteLock(&mceP->afs_memLock,313);
296 /* old directory entry; g.c. */
297 if(size == 0 && mceP->dataSize > memCacheBlkSize) {
298 afs_osi_Free(mceP->data, mceP->dataSize);
299 mceP->data = afs_osi_Alloc(memCacheBlkSize);
300 mceP->dataSize = memCacheBlkSize;
303 if (size < mceP->size)
306 MReleaseWriteLock(&mceP->afs_memLock);
310 int afs_MemCacheStoreProc(register struct rx_call *acall, register struct memCacheEntry *mceP,
311 register afs_int32 alen, struct vcache *avc, int *shouldWake, afs_size_t *abytesToXferP,
312 afs_size_t *abytesXferredP, afs_int32 length)
315 register afs_int32 code;
318 struct iovec *tiov; /* no data copying with iovec */
319 int tnio; /* temp for iovec size */
321 AFS_STATCNT(afs_MemCacheStoreProc);
324 * In this case, alen is *always* the amount of data we'll be trying
327 *(abytesToXferP) = alen;
328 *(abytesXferredP) = 0;
329 #endif /* AFS_NOSTATS */
332 * We need to alloc the iovecs on the heap so that they are "pinned" rather than
333 * declare them on the stack - defect 11272
335 tiov = (struct iovec *) osi_AllocSmallSpace(sizeof(struct iovec)*RX_MAXIOVECS);
337 osi_Panic("afs_MemCacheStoreProc: osi_AllocSmallSpace for iovecs returned NULL\n");
341 /* do this at a higher level now -- it's a parameter */
342 /* for now, only do 'continue from close' code if file fits in one
343 chunk. Could clearly do better: if only one modified chunk
344 then can still do this. can do this on *last* modified chunk */
345 tlen = avc->m.Length-1; /* byte position of last byte we'll store */
347 if (AFS_CHUNK(tlen) != 0) *shouldWake = 0;
348 else *shouldWake = 1;
353 tlen = (alen > AFS_LRALLOCSIZ? AFS_LRALLOCSIZ : alen);
355 code = rx_WritevAlloc(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
358 osi_FreeSmallSpace(tiov);
362 code = afs_MemReadvBlk(mceP, offset, tiov, tnio, tlen);
364 osi_FreeSmallSpace(tiov);
368 code = rx_Writev(acall, tiov, tnio, tlen);
371 (*abytesXferredP) += code;
372 #endif /* AFS_NOSTATS */
374 osi_FreeSmallSpace(tiov);
379 /* if file has been locked on server, can allow store to continue */
380 if (shouldWake && *shouldWake && (rx_GetRemoteStatus(acall) & 1)) {
381 *shouldWake = 0; /* only do this once */
385 osi_FreeSmallSpace(tiov);
389 int afs_MemCacheFetchProc(register struct rx_call *acall, register struct memCacheEntry *mceP,
390 afs_size_t abase, struct dcache *adc, struct vcache *avc, afs_size_t *abytesToXferP,
391 afs_size_t *abytesXferredP, afs_int32 lengthFound)
393 register afs_int32 code;
396 struct iovec *tiov; /* no data copying with iovec */
397 register int tlen, offset=0;
398 int tnio; /* temp for iovec size */
400 AFS_STATCNT(afs_MemCacheFetchProc);
401 length = lengthFound;
402 afs_Trace4(afs_iclSetp, CM_TRACE_MEMFETCH,
403 ICL_TYPE_POINTER, avc,
404 ICL_TYPE_POINTER, mceP,
405 ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(abase),
406 ICL_TYPE_INT32, length);
408 (*abytesToXferP) = 0;
409 (*abytesXferredP) = 0;
410 #endif /* AFS_NOSTATS */
412 * We need to alloc the iovecs on the heap so that they are "pinned" rather than
413 * declare them on the stack - defect 11272
415 tiov = (struct iovec *) osi_AllocSmallSpace(sizeof(struct iovec)*RX_MAXIOVECS);
417 osi_Panic("afs_MemCacheFetchProc: osi_AllocSmallSpace for iovecs returned NULL\n");
419 adc->validPos = abase;
423 code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
424 length = ntohl(length);
426 if (code != sizeof(afs_int32)) {
427 code = rx_Error(acall);
428 osi_FreeSmallSpace(tiov);
429 return (code?code:-1); /* try to return code, not -1 */
433 * The fetch protocol is extended for the AFS/DFS translator
434 * to allow multiple blocks of data, each with its own length,
435 * to be returned. As long as the top bit is set, there are more
438 * We do not do this for AFS file servers because they sometimes
439 * return large negative numbers as the transfer size.
441 if (avc->states & CForeign) {
442 moredata = length & 0x80000000;
443 length &= ~0x80000000;
448 (*abytesToXferP) += length;
449 #endif /* AFS_NOSTATS */
451 tlen = (length > AFS_LRALLOCSIZ? AFS_LRALLOCSIZ : length);
453 code = rx_Readv(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
456 (*abytesXferredP) += code;
457 #endif /* AFS_NOSTATS */
459 afs_Trace3(afs_iclSetp, CM_TRACE_FETCH64READ,
460 ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code,
461 ICL_TYPE_INT32, length);
462 osi_FreeSmallSpace(tiov);
466 afs_MemWritevBlk(mceP, offset, tiov, tnio, tlen);
470 adc->validPos = abase;
471 if (afs_osi_Wakeup(&adc->validPos) == 0)
472 afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAKE,
473 ICL_TYPE_STRING, __FILE__,
474 ICL_TYPE_INT32, __LINE__,
475 ICL_TYPE_POINTER, adc,
476 ICL_TYPE_INT32, adc->dflags);
479 /* max of two sizes */
480 osi_FreeSmallSpace(tiov);
485 void shutdown_memcache(void)
489 if (cacheDiskType != AFS_FCACHE_TYPE_MEM)
491 memCacheBlkSize = 8192;
492 for (index = 0; index < memMaxBlkNumber; index++) {
493 LOCK_INIT(&((memCache+index)->afs_memLock), "afs_memLock");
494 afs_osi_Free((memCache+index)->data, (memCache+index)->dataSize);
496 afs_osi_Free((char *)memCache, memMaxBlkNumber * sizeof(struct memCacheEntry));