cc7bb90590e6f1e4934f7cc25a13fe7e075d2906
[openafs.git] / src / afs / afs_memcache.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 #include <afsconfig.h>
11 #include "afs/param.h"
12
13
14 #include "afs/sysincludes.h"    /* Standard vendor system headers */
15 #ifndef AFS_LINUX22_ENV
16 #include "rpc/types.h"
17 #endif
18 #ifdef  AFS_OSF_ENV
19 #undef kmem_alloc
20 #undef kmem_free
21 #undef mem_alloc
22 #undef mem_free
23 #undef register
24 #endif /* AFS_OSF_ENV */
25 #include "afsincludes.h"        /* Afs-based standard headers */
26 #include "afs/afs_stats.h"      /* statistics */
27
28 /* memory cache routines */
29 static struct memCacheEntry *memCache;
30 static int memCacheBlkSize = 8192;
31 static int memMaxBlkNumber = 0;
32 static int memAllocMaySleep = 0;
33
34 extern int cacheDiskType;
35
36 int
37 afs_InitMemCache(int blkCount, int blkSize, int flags)
38 {
39     int index;
40
41     AFS_STATCNT(afs_InitMemCache);
42     if (blkSize)
43         memCacheBlkSize = blkSize;
44
45     memMaxBlkNumber = blkCount;
46     memCache = (struct memCacheEntry *)
47         afs_osi_Alloc(memMaxBlkNumber * sizeof(struct memCacheEntry));
48     if (flags & AFSCALL_INIT_MEMCACHE_SLEEP) {
49         memAllocMaySleep = 1;
50     }
51
52     for (index = 0; index < memMaxBlkNumber; index++) {
53         char *blk;
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);
59         } else {
60             blk = afs_osi_Alloc_NoSleep(memCacheBlkSize);
61         }
62         if (blk == NULL)
63             goto nomem;
64         (memCache + index)->data = blk;
65         memset((memCache + index)->data, 0, memCacheBlkSize);
66     }
67 #if defined(AFS_SGI62_ENV) || defined(AFS_HAVE_VXFS)
68     afs_InitDualFSCacheOps((struct vnode *)0);
69 #endif
70
71     return 0;
72
73   nomem:
74     printf("afsd:  memCache allocation failure at %d KB.\n",
75            (index * memCacheBlkSize) / 1024);
76     while (--index >= 0) {
77         afs_osi_Free((memCache + index)->data, memCacheBlkSize);
78         (memCache + index)->data = NULL;
79     }
80     return ENOMEM;
81
82 }
83
84 int
85 afs_MemCacheClose(struct osi_file *file)
86 {
87     return 0;
88 }
89
90 void *
91 afs_MemCacheOpen(afs_dcache_id_t *ainode)
92 {
93     struct memCacheEntry *mep;
94
95     if (ainode->mem < 0 || ainode->mem > memMaxBlkNumber) {
96         osi_Panic("afs_MemCacheOpen: invalid block #");
97     }
98     mep = (memCache + ainode->mem);
99     afs_Trace3(afs_iclSetp, CM_TRACE_MEMOPEN, ICL_TYPE_INT32, ainode->mem,
100                ICL_TYPE_POINTER, mep, ICL_TYPE_POINTER, mep ? mep->data : 0);
101     return (void *)mep;
102 }
103
104 /*
105  * this routine simulates a read in the Memory Cache 
106  */
107 int
108 afs_MemReadBlk(register struct osi_file *fP, int offset, void *dest,
109                int size)
110 {
111     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
112     int bytesRead;
113
114     MObtainReadLock(&mceP->afs_memLock);
115     AFS_STATCNT(afs_MemReadBlk);
116     if (offset < 0) {
117         MReleaseReadLock(&mceP->afs_memLock);
118         return 0;
119     }
120     /* use min of bytes in buffer or requested size */
121     bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
122
123     if (bytesRead > 0) {
124         AFS_GUNLOCK();
125         memcpy(dest, mceP->data + offset, bytesRead);
126         AFS_GLOCK();
127     } else
128         bytesRead = 0;
129
130     MReleaseReadLock(&mceP->afs_memLock);
131     return bytesRead;
132 }
133
134 /*
135  * this routine simulates a readv in the Memory Cache 
136  */
137 int
138 afs_MemReadvBlk(register struct memCacheEntry *mceP, int offset,
139                 struct iovec *iov, int nio, int size)
140 {
141     int i;
142     int bytesRead;
143     int bytesToRead;
144
145     MObtainReadLock(&mceP->afs_memLock);
146     AFS_STATCNT(afs_MemReadBlk);
147     if (offset < 0) {
148         MReleaseReadLock(&mceP->afs_memLock);
149         return 0;
150     }
151     /* use min of bytes in buffer or requested size */
152     bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
153
154     if (bytesRead > 0) {
155         for (i = 0, size = bytesRead; i < nio && size > 0; i++) {
156             bytesToRead = (size < iov[i].iov_len) ? size : iov[i].iov_len;
157             AFS_GUNLOCK();
158             memcpy(iov[i].iov_base, mceP->data + offset, bytesToRead);
159             AFS_GLOCK();
160             offset += bytesToRead;
161             size -= bytesToRead;
162         }
163         bytesRead -= size;
164     } else
165         bytesRead = 0;
166
167     MReleaseReadLock(&mceP->afs_memLock);
168     return bytesRead;
169 }
170
171 int
172 afs_MemReadUIO(afs_dcache_id_t *ainode, struct uio *uioP)
173 {
174     register struct memCacheEntry *mceP =
175         (struct memCacheEntry *)afs_MemCacheOpen(ainode);
176     int length = mceP->size - AFS_UIO_OFFSET(uioP);
177     afs_int32 code;
178
179     AFS_STATCNT(afs_MemReadUIO);
180     MObtainReadLock(&mceP->afs_memLock);
181     length = (length < AFS_UIO_RESID(uioP)) ? length : AFS_UIO_RESID(uioP);
182     AFS_UIOMOVE(mceP->data + AFS_UIO_OFFSET(uioP), length, UIO_READ, uioP, code);
183     MReleaseReadLock(&mceP->afs_memLock);
184     return code;
185 }
186
187 /*XXX: this extends a block arbitrarily to support big directories */
188 int
189 afs_MemWriteBlk(register struct osi_file *fP, int offset, void *src,
190                 int size)
191 {
192     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
193     AFS_STATCNT(afs_MemWriteBlk);
194     MObtainWriteLock(&mceP->afs_memLock, 560);
195     if (size + offset > mceP->dataSize) {
196         char *oldData = mceP->data;
197
198         if (memAllocMaySleep) {
199             mceP->data = afs_osi_Alloc(size + offset);
200         } else {
201             mceP->data = afs_osi_Alloc_NoSleep(size + offset);
202         }
203         if (mceP->data == NULL) {       /* no available memory */
204             mceP->data = oldData;       /* revert back change that was made */
205             MReleaseWriteLock(&mceP->afs_memLock);
206             afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
207                      size + offset);
208             return -ENOMEM;
209         }
210
211         /* may overlap, but this is OK */
212         AFS_GUNLOCK();
213         memcpy(mceP->data, oldData, mceP->size);
214         AFS_GLOCK();
215         afs_osi_Free(oldData, mceP->dataSize);
216         mceP->dataSize = size + offset;
217     }
218     AFS_GUNLOCK();
219     if (mceP->size < offset)
220         memset(mceP->data + mceP->size, 0, offset - mceP->size);
221     memcpy(mceP->data + offset, src, size);
222     AFS_GLOCK();
223     mceP->size = (size + offset < mceP->size) ? mceP->size : size + offset;
224
225     MReleaseWriteLock(&mceP->afs_memLock);
226     return size;
227 }
228
229 /*XXX: this extends a block arbitrarily to support big directories */
230 int
231 afs_MemWritevBlk(register struct memCacheEntry *mceP, int offset,
232                  struct iovec *iov, int nio, int size)
233 {
234     int i;
235     int bytesWritten;
236     int bytesToWrite;
237     AFS_STATCNT(afs_MemWriteBlk);
238     MObtainWriteLock(&mceP->afs_memLock, 561);
239     if (offset + size > mceP->dataSize) {
240         char *oldData = mceP->data;
241
242         mceP->data = afs_osi_Alloc(size + offset);
243         if (mceP->data == NULL) {       /* no available memory */
244             mceP->data = oldData;       /* revert back change that was made */
245             MReleaseWriteLock(&mceP->afs_memLock);
246             afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
247                      size + offset);
248             return -ENOMEM;
249         }
250
251         /* may overlap, but this is OK */
252         AFS_GUNLOCK();
253         memcpy(mceP->data, oldData, mceP->size);
254         AFS_GLOCK();
255         afs_osi_Free(oldData, mceP->dataSize);
256         mceP->dataSize = size + offset;
257     }
258     AFS_GUNLOCK();
259     if (mceP->size < offset)
260         memset(mceP->data + mceP->size, 0, offset - mceP->size);
261     for (bytesWritten = 0, i = 0; i < nio && size > 0; i++) {
262         bytesToWrite = (size < iov[i].iov_len) ? size : iov[i].iov_len;
263         memcpy(mceP->data + offset, iov[i].iov_base, bytesToWrite);
264         offset += bytesToWrite;
265         bytesWritten += bytesToWrite;
266         size -= bytesToWrite;
267     }
268     mceP->size = (offset < mceP->size) ? mceP->size : offset;
269     AFS_GLOCK();
270
271     MReleaseWriteLock(&mceP->afs_memLock);
272     return bytesWritten;
273 }
274
275 int
276 afs_MemWriteUIO(afs_dcache_id_t *ainode, struct uio *uioP)
277 {
278     register struct memCacheEntry *mceP =
279         (struct memCacheEntry *)afs_MemCacheOpen(ainode);
280     afs_int32 code;
281
282     AFS_STATCNT(afs_MemWriteUIO);
283     MObtainWriteLock(&mceP->afs_memLock, 312);
284     if (AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP) > mceP->dataSize) {
285         char *oldData = mceP->data;
286
287         mceP->data = afs_osi_Alloc(AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP));
288         if (mceP->data == NULL) {       /* no available memory */
289             mceP->data = oldData;       /* revert back change that was made */
290             MReleaseWriteLock(&mceP->afs_memLock);
291             afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
292                      AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP));
293             return -ENOMEM;
294         }
295
296         AFS_GUNLOCK();
297         memcpy(mceP->data, oldData, mceP->size);
298         AFS_GLOCK();
299
300         afs_osi_Free(oldData, mceP->dataSize);
301         mceP->dataSize = AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP);
302     }
303     if (mceP->size < AFS_UIO_OFFSET(uioP))
304         memset(mceP->data + mceP->size, 0,
305                (int)(AFS_UIO_OFFSET(uioP) - mceP->size));
306     AFS_UIOMOVE(mceP->data + AFS_UIO_OFFSET(uioP), AFS_UIO_RESID(uioP), UIO_WRITE,
307                 uioP, code);
308     if (AFS_UIO_OFFSET(uioP) > mceP->size)
309         mceP->size = AFS_UIO_OFFSET(uioP);
310
311     MReleaseWriteLock(&mceP->afs_memLock);
312     return code;
313 }
314
315 int
316 afs_MemCacheTruncate(register struct osi_file *fP, int size)
317 {
318     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
319     AFS_STATCNT(afs_MemCacheTruncate);
320
321     MObtainWriteLock(&mceP->afs_memLock, 313);
322     /* old directory entry; g.c. */
323     if (size == 0 && mceP->dataSize > memCacheBlkSize) {
324         char *oldData = mceP->data;
325         mceP->data = afs_osi_Alloc(memCacheBlkSize);
326         if (mceP->data == NULL) {       /* no available memory */
327             mceP->data = oldData;
328             MReleaseWriteLock(&mceP->afs_memLock);
329             afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
330                      memCacheBlkSize);
331         } else {
332             afs_osi_Free(oldData, mceP->dataSize);
333             mceP->dataSize = memCacheBlkSize;
334         }
335     }
336
337     if (size < mceP->size)
338         mceP->size = size;
339
340     MReleaseWriteLock(&mceP->afs_memLock);
341     return 0;
342 }
343
344 int
345 afs_MemCacheStoreProc(register struct rx_call *acall,
346                       register struct osi_file *fP,
347                       register afs_int32 alen, struct vcache *avc,
348                       int *shouldWake, afs_size_t * abytesToXferP,
349                       afs_size_t * abytesXferredP)
350 {
351     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
352
353     register afs_int32 code;
354     register int tlen;
355     int offset = 0;
356     struct iovec *tiov;         /* no data copying with iovec */
357     int tnio;                   /* temp for iovec size */
358
359     AFS_STATCNT(afs_MemCacheStoreProc);
360 #ifndef AFS_NOSTATS
361     /*
362      * In this case, alen is *always* the amount of data we'll be trying
363      * to ship here.
364      */
365     *(abytesToXferP) = alen;
366     *(abytesXferredP) = 0;
367 #endif /* AFS_NOSTATS */
368
369     /* 
370      * We need to alloc the iovecs on the heap so that they are "pinned" rather than
371      * declare them on the stack - defect 11272
372      */
373     tiov =
374         (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec) *
375                                             RX_MAXIOVECS);
376     if (!tiov) {
377         osi_Panic
378             ("afs_MemCacheStoreProc: osi_AllocSmallSpace for iovecs returned NULL\n");
379     }
380 #ifdef notdef
381     /* do this at a higher level now -- it's a parameter */
382     /* for now, only do 'continue from close' code if file fits in one
383      * chunk.  Could clearly do better: if only one modified chunk
384      * then can still do this.  can do this on *last* modified chunk */
385     tlen = avc->f.m.Length - 1; /* byte position of last byte we'll store */
386     if (shouldWake) {
387         if (AFS_CHUNK(tlen) != 0)
388             *shouldWake = 0;
389         else
390             *shouldWake = 1;
391     }
392 #endif /* notdef */
393
394     while (alen > 0) {
395         tlen = (alen > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : alen);
396         RX_AFS_GUNLOCK();
397         code = rx_WritevAlloc(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
398         RX_AFS_GLOCK();
399         if (code <= 0) {
400             code = rx_Error(acall);
401             osi_FreeSmallSpace(tiov);
402             return code ? code : -33;
403         }
404         tlen = code;
405         code = afs_MemReadvBlk(mceP, offset, tiov, tnio, tlen);
406         if (code != tlen) {
407             osi_FreeSmallSpace(tiov);
408             return -33;
409         }
410         RX_AFS_GUNLOCK();
411         code = rx_Writev(acall, tiov, tnio, tlen);
412         RX_AFS_GLOCK();
413 #ifndef AFS_NOSTATS
414         (*abytesXferredP) += code;
415 #endif /* AFS_NOSTATS */
416         if (code != tlen) {
417             code = rx_Error(acall);
418             osi_FreeSmallSpace(tiov);
419             return code ? code : -33;
420         }
421         offset += tlen;
422         alen -= tlen;
423         /* if file has been locked on server, can allow store to continue */
424         if (shouldWake && *shouldWake && (rx_GetRemoteStatus(acall) & 1)) {
425             *shouldWake = 0;    /* only do this once */
426             afs_wakeup(avc);
427         }
428     }
429     osi_FreeSmallSpace(tiov);
430     return 0;
431 }
432
433 int
434 afs_MemCacheFetchProc(register struct rx_call *acall,
435                       register struct osi_file *fP, afs_size_t abase,
436                       struct dcache *adc, struct vcache *avc,
437                       afs_size_t * abytesToXferP, afs_size_t * abytesXferredP,
438                       afs_int32 lengthFound)
439 {
440     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
441     register afs_int32 code;
442     afs_int32 length;
443     int moredata = 0;
444     struct iovec *tiov;         /* no data copying with iovec */
445     register int tlen, offset = 0;
446     int tnio;                   /* temp for iovec size */
447
448     AFS_STATCNT(afs_MemCacheFetchProc);
449     length = lengthFound;
450     afs_Trace4(afs_iclSetp, CM_TRACE_MEMFETCH, ICL_TYPE_POINTER, avc,
451                ICL_TYPE_POINTER, mceP, ICL_TYPE_OFFSET,
452                ICL_HANDLE_OFFSET(abase), ICL_TYPE_INT32, length);
453 #ifndef AFS_NOSTATS
454     (*abytesToXferP) = 0;
455     (*abytesXferredP) = 0;
456 #endif /* AFS_NOSTATS */
457     /* 
458      * We need to alloc the iovecs on the heap so that they are "pinned" rather than
459      * declare them on the stack - defect 11272
460      */
461     tiov =
462         (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec) *
463                                             RX_MAXIOVECS);
464     if (!tiov) {
465         osi_Panic
466             ("afs_MemCacheFetchProc: osi_AllocSmallSpace for iovecs returned NULL\n");
467     }
468     adc->validPos = abase;
469     do {
470         if (moredata) {
471             RX_AFS_GUNLOCK();
472             code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
473             length = ntohl(length);
474             RX_AFS_GLOCK();
475             if (code != sizeof(afs_int32)) {
476                 code = rx_Error(acall);
477                 osi_FreeSmallSpace(tiov);
478                 return (code ? code : -1);      /* try to return code, not -1 */
479             }
480         }
481         /*
482          * The fetch protocol is extended for the AFS/DFS translator
483          * to allow multiple blocks of data, each with its own length,
484          * to be returned. As long as the top bit is set, there are more
485          * blocks expected.
486          *
487          * We do not do this for AFS file servers because they sometimes
488          * return large negative numbers as the transfer size.
489          */
490         if (avc->f.states & CForeign) {
491             moredata = length & 0x80000000;
492             length &= ~0x80000000;
493         } else {
494             moredata = 0;
495         }
496 #ifndef AFS_NOSTATS
497         (*abytesToXferP) += length;
498 #endif /* AFS_NOSTATS */
499         while (length > 0) {
500             tlen = (length > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : length);
501             RX_AFS_GUNLOCK();
502             code = rx_Readv(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
503             RX_AFS_GLOCK();
504 #ifndef AFS_NOSTATS
505             (*abytesXferredP) += code;
506 #endif /* AFS_NOSTATS */
507             if (code <= 0) {
508                 afs_Trace3(afs_iclSetp, CM_TRACE_FETCH64READ,
509                            ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code,
510                            ICL_TYPE_INT32, length);
511                 osi_FreeSmallSpace(tiov);
512                 return -34;
513             }
514             tlen = code;
515             afs_MemWritevBlk(mceP, offset, tiov, tnio, tlen);
516             offset += tlen;
517             abase += tlen;
518             length -= tlen;
519             adc->validPos = abase;
520             if (afs_osi_Wakeup(&adc->validPos) == 0)
521                 afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAKE, ICL_TYPE_STRING,
522                            __FILE__, ICL_TYPE_INT32, __LINE__,
523                            ICL_TYPE_POINTER, adc, ICL_TYPE_INT32,
524                            adc->dflags);
525         }
526     } while (moredata);
527     /* max of two sizes */
528     osi_FreeSmallSpace(tiov);
529     return 0;
530 }
531
532
533 void
534 shutdown_memcache(void)
535 {
536     register int index;
537
538     if (cacheDiskType != AFS_FCACHE_TYPE_MEM)
539         return;
540     memCacheBlkSize = 8192;
541     for (index = 0; index < memMaxBlkNumber; index++) {
542         LOCK_INIT(&((memCache + index)->afs_memLock), "afs_memLock");
543         afs_osi_Free((memCache + index)->data, (memCache + index)->dataSize);
544     }
545     afs_osi_Free((char *)memCache,
546                  memMaxBlkNumber * sizeof(struct memCacheEntry));
547     memMaxBlkNumber = 0;
548 }