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"
16 #include "afs/sysincludes.h" /* Standard vendor system headers */
17 #ifndef AFS_LINUX22_ENV
18 #include "rpc/types.h"
26 #endif /* AFS_OSF_ENV */
27 #include "afsincludes.h" /* Afs-based standard headers */
28 #include "afs/afs_stats.h" /* statistics */
30 /* memory cache routines */
31 static struct memCacheEntry *memCache;
32 static int memCacheBlkSize = 8192;
33 static int memMaxBlkNumber = 0;
34 static int memAllocMaySleep = 0;
36 extern int cacheDiskType;
39 afs_InitMemCache(int blkCount, int blkSize, int flags)
43 AFS_STATCNT(afs_InitMemCache);
45 memCacheBlkSize = blkSize;
47 memMaxBlkNumber = blkCount;
48 memCache = (struct memCacheEntry *)
49 afs_osi_Alloc(memMaxBlkNumber * sizeof(struct memCacheEntry));
50 if (flags & AFSCALL_INIT_MEMCACHE_SLEEP) {
54 for (index = 0; index < memMaxBlkNumber; index++) {
56 (memCache + index)->size = 0;
57 (memCache + index)->dataSize = memCacheBlkSize;
58 LOCK_INIT(&((memCache + index)->afs_memLock), "afs_memLock");
59 if (memAllocMaySleep) {
60 blk = afs_osi_Alloc(memCacheBlkSize);
62 blk = afs_osi_Alloc_NoSleep(memCacheBlkSize);
66 (memCache + index)->data = blk;
67 memset((memCache + index)->data, 0, memCacheBlkSize);
69 #if defined(AFS_SGI62_ENV) || defined(AFS_HAVE_VXFS)
70 afs_InitDualFSCacheOps((struct vnode *)0);
76 printf("afsd: memCache allocation failure at %d KB.\n",
77 (index * memCacheBlkSize) / 1024);
78 while (--index >= 0) {
79 afs_osi_Free((memCache + index)->data, memCacheBlkSize);
80 (memCache + index)->data = NULL;
87 afs_MemCacheClose(struct osi_file *file)
92 #if defined(AFS_SUN57_64BIT_ENV) || defined(AFS_SGI62_ENV)
94 afs_MemCacheOpen(ino_t blkno)
97 afs_MemCacheOpen(afs_int32 blkno)
100 struct memCacheEntry *mep;
102 if (blkno < 0 || blkno > memMaxBlkNumber) {
103 osi_Panic("afs_MemCacheOpen: invalid block #");
105 mep = (memCache + blkno);
106 afs_Trace3(afs_iclSetp, CM_TRACE_MEMOPEN, ICL_TYPE_INT32, blkno,
107 ICL_TYPE_POINTER, mep, ICL_TYPE_POINTER, mep ? mep->data : 0);
112 * this routine simulates a read in the Memory Cache
115 afs_MemReadBlk(register struct osi_file *fP, int offset, void *dest,
118 register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
121 MObtainReadLock(&mceP->afs_memLock);
122 AFS_STATCNT(afs_MemReadBlk);
124 MReleaseReadLock(&mceP->afs_memLock);
127 /* use min of bytes in buffer or requested size */
128 bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
132 memcpy(dest, mceP->data + offset, bytesRead);
137 MReleaseReadLock(&mceP->afs_memLock);
142 * this routine simulates a readv in the Memory Cache
145 afs_MemReadvBlk(register struct memCacheEntry *mceP, int offset,
146 struct iovec *iov, int nio, int size)
152 MObtainReadLock(&mceP->afs_memLock);
153 AFS_STATCNT(afs_MemReadBlk);
155 MReleaseReadLock(&mceP->afs_memLock);
158 /* use min of bytes in buffer or requested size */
159 bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
162 for (i = 0, size = bytesRead; i < nio && size > 0; i++) {
163 bytesToRead = (size < iov[i].iov_len) ? size : iov[i].iov_len;
165 memcpy(iov[i].iov_base, mceP->data + offset, bytesToRead);
167 offset += bytesToRead;
174 MReleaseReadLock(&mceP->afs_memLock);
179 afs_MemReadUIO(ino_t blkno, struct uio *uioP)
181 register struct memCacheEntry *mceP =
182 (struct memCacheEntry *)afs_MemCacheOpen(blkno);
183 int length = mceP->size - AFS_UIO_OFFSET(uioP);
186 AFS_STATCNT(afs_MemReadUIO);
187 MObtainReadLock(&mceP->afs_memLock);
188 length = (length < AFS_UIO_RESID(uioP)) ? length : AFS_UIO_RESID(uioP);
189 AFS_UIOMOVE(mceP->data + AFS_UIO_OFFSET(uioP), length, UIO_READ, uioP, code);
190 MReleaseReadLock(&mceP->afs_memLock);
194 /*XXX: this extends a block arbitrarily to support big directories */
196 afs_MemWriteBlk(register struct osi_file *fP, int offset, void *src,
199 register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
200 AFS_STATCNT(afs_MemWriteBlk);
201 MObtainWriteLock(&mceP->afs_memLock, 560);
202 if (size + offset > mceP->dataSize) {
203 char *oldData = mceP->data;
205 if (memAllocMaySleep) {
206 mceP->data = afs_osi_Alloc(size + offset);
208 mceP->data = afs_osi_Alloc_NoSleep(size + offset);
210 if (mceP->data == NULL) { /* no available memory */
211 mceP->data = oldData; /* revert back change that was made */
212 MReleaseWriteLock(&mceP->afs_memLock);
213 afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
218 /* may overlap, but this is OK */
220 memcpy(mceP->data, oldData, mceP->size);
222 afs_osi_Free(oldData, mceP->dataSize);
223 mceP->dataSize = size + offset;
226 if (mceP->size < offset)
227 memset(mceP->data + mceP->size, 0, offset - mceP->size);
228 memcpy(mceP->data + offset, src, size);
230 mceP->size = (size + offset < mceP->size) ? mceP->size : size + offset;
232 MReleaseWriteLock(&mceP->afs_memLock);
236 /*XXX: this extends a block arbitrarily to support big directories */
238 afs_MemWritevBlk(register struct memCacheEntry *mceP, int offset,
239 struct iovec *iov, int nio, int size)
244 AFS_STATCNT(afs_MemWriteBlk);
245 MObtainWriteLock(&mceP->afs_memLock, 561);
246 if (offset + size > mceP->dataSize) {
247 char *oldData = mceP->data;
249 mceP->data = afs_osi_Alloc(size + offset);
250 if (mceP->data == NULL) { /* no available memory */
251 mceP->data = oldData; /* revert back change that was made */
252 MReleaseWriteLock(&mceP->afs_memLock);
253 afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
258 /* may overlap, but this is OK */
260 memcpy(mceP->data, oldData, mceP->size);
262 afs_osi_Free(oldData, mceP->dataSize);
263 mceP->dataSize = size + offset;
266 if (mceP->size < offset)
267 memset(mceP->data + mceP->size, 0, offset - mceP->size);
268 for (bytesWritten = 0, i = 0; i < nio && size > 0; i++) {
269 bytesToWrite = (size < iov[i].iov_len) ? size : iov[i].iov_len;
270 memcpy(mceP->data + offset, iov[i].iov_base, bytesToWrite);
271 offset += bytesToWrite;
272 bytesWritten += bytesToWrite;
273 size -= bytesToWrite;
275 mceP->size = (offset < mceP->size) ? mceP->size : offset;
278 MReleaseWriteLock(&mceP->afs_memLock);
283 afs_MemWriteUIO(ino_t blkno, struct uio *uioP)
285 register struct memCacheEntry *mceP =
286 (struct memCacheEntry *)afs_MemCacheOpen(blkno);
289 AFS_STATCNT(afs_MemWriteUIO);
290 MObtainWriteLock(&mceP->afs_memLock, 312);
291 if (AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP) > mceP->dataSize) {
292 char *oldData = mceP->data;
294 mceP->data = afs_osi_Alloc(AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP));
295 if (mceP->data == NULL) { /* no available memory */
296 mceP->data = oldData; /* revert back change that was made */
297 MReleaseWriteLock(&mceP->afs_memLock);
298 afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
299 AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP));
304 memcpy(mceP->data, oldData, mceP->size);
307 afs_osi_Free(oldData, mceP->dataSize);
308 mceP->dataSize = AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP);
310 if (mceP->size < AFS_UIO_OFFSET(uioP))
311 memset(mceP->data + mceP->size, 0,
312 (int)(AFS_UIO_OFFSET(uioP) - mceP->size));
313 AFS_UIOMOVE(mceP->data + AFS_UIO_OFFSET(uioP), AFS_UIO_RESID(uioP), UIO_WRITE,
315 if (AFS_UIO_OFFSET(uioP) > mceP->size)
316 mceP->size = AFS_UIO_OFFSET(uioP);
318 MReleaseWriteLock(&mceP->afs_memLock);
323 afs_MemCacheTruncate(register struct osi_file *fP, int size)
325 register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
326 AFS_STATCNT(afs_MemCacheTruncate);
328 MObtainWriteLock(&mceP->afs_memLock, 313);
329 /* old directory entry; g.c. */
330 if (size == 0 && mceP->dataSize > memCacheBlkSize) {
331 char *oldData = mceP->data;
332 mceP->data = afs_osi_Alloc(memCacheBlkSize);
333 if (mceP->data == NULL) { /* no available memory */
334 mceP->data = oldData;
335 MReleaseWriteLock(&mceP->afs_memLock);
336 afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
339 afs_osi_Free(oldData, mceP->dataSize);
340 mceP->dataSize = memCacheBlkSize;
344 if (size < mceP->size)
347 MReleaseWriteLock(&mceP->afs_memLock);
352 afs_MemCacheStoreProc(register struct rx_call *acall,
353 register struct osi_file *fP,
354 register afs_int32 alen, struct vcache *avc,
355 int *shouldWake, afs_size_t * abytesToXferP,
356 afs_size_t * abytesXferredP)
358 register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
360 register afs_int32 code;
363 struct iovec *tiov; /* no data copying with iovec */
364 int tnio; /* temp for iovec size */
366 AFS_STATCNT(afs_MemCacheStoreProc);
369 * In this case, alen is *always* the amount of data we'll be trying
372 *(abytesToXferP) = alen;
373 *(abytesXferredP) = 0;
374 #endif /* AFS_NOSTATS */
377 * We need to alloc the iovecs on the heap so that they are "pinned" rather than
378 * declare them on the stack - defect 11272
381 (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec) *
385 ("afs_MemCacheStoreProc: osi_AllocSmallSpace for iovecs returned NULL\n");
388 /* do this at a higher level now -- it's a parameter */
389 /* for now, only do 'continue from close' code if file fits in one
390 * chunk. Could clearly do better: if only one modified chunk
391 * then can still do this. can do this on *last* modified chunk */
392 tlen = avc->m.Length - 1; /* byte position of last byte we'll store */
394 if (AFS_CHUNK(tlen) != 0)
402 tlen = (alen > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : alen);
404 code = rx_WritevAlloc(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
407 code = rx_Error(acall);
408 osi_FreeSmallSpace(tiov);
409 return code ? code : -33;
412 code = afs_MemReadvBlk(mceP, offset, tiov, tnio, tlen);
414 osi_FreeSmallSpace(tiov);
418 code = rx_Writev(acall, tiov, tnio, tlen);
421 (*abytesXferredP) += code;
422 #endif /* AFS_NOSTATS */
424 code = rx_Error(acall);
425 osi_FreeSmallSpace(tiov);
426 return code ? code : -33;
430 /* if file has been locked on server, can allow store to continue */
431 if (shouldWake && *shouldWake && (rx_GetRemoteStatus(acall) & 1)) {
432 *shouldWake = 0; /* only do this once */
436 osi_FreeSmallSpace(tiov);
441 afs_MemCacheFetchProc(register struct rx_call *acall,
442 register struct osi_file *fP, afs_size_t abase,
443 struct dcache *adc, struct vcache *avc,
444 afs_size_t * abytesToXferP, afs_size_t * abytesXferredP,
445 afs_int32 lengthFound)
447 register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
448 register afs_int32 code;
451 struct iovec *tiov; /* no data copying with iovec */
452 register int tlen, offset = 0;
453 int tnio; /* temp for iovec size */
455 AFS_STATCNT(afs_MemCacheFetchProc);
456 length = lengthFound;
457 afs_Trace4(afs_iclSetp, CM_TRACE_MEMFETCH, ICL_TYPE_POINTER, avc,
458 ICL_TYPE_POINTER, mceP, ICL_TYPE_OFFSET,
459 ICL_HANDLE_OFFSET(abase), ICL_TYPE_INT32, length);
461 (*abytesToXferP) = 0;
462 (*abytesXferredP) = 0;
463 #endif /* AFS_NOSTATS */
465 * We need to alloc the iovecs on the heap so that they are "pinned" rather than
466 * declare them on the stack - defect 11272
469 (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec) *
473 ("afs_MemCacheFetchProc: osi_AllocSmallSpace for iovecs returned NULL\n");
475 adc->validPos = abase;
479 code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
480 length = ntohl(length);
482 if (code != sizeof(afs_int32)) {
483 code = rx_Error(acall);
484 osi_FreeSmallSpace(tiov);
485 return (code ? code : -1); /* try to return code, not -1 */
489 * The fetch protocol is extended for the AFS/DFS translator
490 * to allow multiple blocks of data, each with its own length,
491 * to be returned. As long as the top bit is set, there are more
494 * We do not do this for AFS file servers because they sometimes
495 * return large negative numbers as the transfer size.
497 if (avc->states & CForeign) {
498 moredata = length & 0x80000000;
499 length &= ~0x80000000;
504 (*abytesToXferP) += length;
505 #endif /* AFS_NOSTATS */
507 tlen = (length > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : length);
509 code = rx_Readv(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
512 (*abytesXferredP) += code;
513 #endif /* AFS_NOSTATS */
515 afs_Trace3(afs_iclSetp, CM_TRACE_FETCH64READ,
516 ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code,
517 ICL_TYPE_INT32, length);
518 osi_FreeSmallSpace(tiov);
522 afs_MemWritevBlk(mceP, offset, tiov, tnio, tlen);
526 adc->validPos = abase;
527 if (afs_osi_Wakeup(&adc->validPos) == 0)
528 afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAKE, ICL_TYPE_STRING,
529 __FILE__, ICL_TYPE_INT32, __LINE__,
530 ICL_TYPE_POINTER, adc, ICL_TYPE_INT32,
534 /* max of two sizes */
535 osi_FreeSmallSpace(tiov);
541 shutdown_memcache(void)
545 if (cacheDiskType != AFS_FCACHE_TYPE_MEM)
547 memCacheBlkSize = 8192;
548 for (index = 0; index < memMaxBlkNumber; index++) {
549 LOCK_INIT(&((memCache + index)->afs_memLock), "afs_memLock");
550 afs_osi_Free((memCache + index)->data, (memCache + index)->dataSize);
552 afs_osi_Free((char *)memCache,
553 memMaxBlkNumber * sizeof(struct memCacheEntry));