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