DEVEL15-linux-2624-20071123
[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_Trace3(afs_iclSetp, CM_TRACE_MEMOPEN, ICL_TYPE_INT32, blkno,
107                ICL_TYPE_POINTER, mep, ICL_TYPE_POINTER, mep ? mep->data : 0);
108     return (void *)mep;
109 }
110
111 /*
112  * this routine simulates a read in the Memory Cache 
113  */
114 int
115 afs_MemReadBlk(register struct osi_file *fP, int offset, void *dest,
116                int size)
117 {
118     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
119     int bytesRead;
120
121     MObtainReadLock(&mceP->afs_memLock);
122     AFS_STATCNT(afs_MemReadBlk);
123     if (offset < 0) {
124         MReleaseReadLock(&mceP->afs_memLock);
125         return 0;
126     }
127     /* use min of bytes in buffer or requested size */
128     bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
129
130     if (bytesRead > 0) {
131         AFS_GUNLOCK();
132         memcpy(dest, mceP->data + offset, bytesRead);
133         AFS_GLOCK();
134     } else
135         bytesRead = 0;
136
137     MReleaseReadLock(&mceP->afs_memLock);
138     return bytesRead;
139 }
140
141 /*
142  * this routine simulates a readv in the Memory Cache 
143  */
144 int
145 afs_MemReadvBlk(register struct memCacheEntry *mceP, int offset,
146                 struct iovec *iov, int nio, int size)
147 {
148     int i;
149     int bytesRead;
150     int bytesToRead;
151
152     MObtainReadLock(&mceP->afs_memLock);
153     AFS_STATCNT(afs_MemReadBlk);
154     if (offset < 0) {
155         MReleaseReadLock(&mceP->afs_memLock);
156         return 0;
157     }
158     /* use min of bytes in buffer or requested size */
159     bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
160
161     if (bytesRead > 0) {
162         for (i = 0, size = bytesRead; i < nio && size > 0; i++) {
163             bytesToRead = (size < iov[i].iov_len) ? size : iov[i].iov_len;
164             AFS_GUNLOCK();
165             memcpy(iov[i].iov_base, mceP->data + offset, bytesToRead);
166             AFS_GLOCK();
167             offset += bytesToRead;
168             size -= bytesToRead;
169         }
170         bytesRead -= size;
171     } else
172         bytesRead = 0;
173
174     MReleaseReadLock(&mceP->afs_memLock);
175     return bytesRead;
176 }
177
178 int
179 afs_MemReadUIO(ino_t blkno, struct uio *uioP)
180 {
181     register struct memCacheEntry *mceP =
182         (struct memCacheEntry *)afs_MemCacheOpen(blkno);
183     int length = mceP->size - AFS_UIO_OFFSET(uioP);
184     afs_int32 code;
185
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);
191     return code;
192 }
193
194 /*XXX: this extends a block arbitrarily to support big directories */
195 int
196 afs_MemWriteBlk(register struct osi_file *fP, int offset, void *src,
197                 int size)
198 {
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;
204
205         if (memAllocMaySleep) {
206             mceP->data = afs_osi_Alloc(size + offset);
207         } else {
208             mceP->data = afs_osi_Alloc_NoSleep(size + offset);
209         }
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",
214                      size + offset);
215             return -ENOMEM;
216         }
217
218         /* may overlap, but this is OK */
219         AFS_GUNLOCK();
220         memcpy(mceP->data, oldData, mceP->size);
221         AFS_GLOCK();
222         afs_osi_Free(oldData, mceP->dataSize);
223         mceP->dataSize = size + offset;
224     }
225     AFS_GUNLOCK();
226     if (mceP->size < offset)
227         memset(mceP->data + mceP->size, 0, offset - mceP->size);
228     memcpy(mceP->data + offset, src, size);
229     AFS_GLOCK();
230     mceP->size = (size + offset < mceP->size) ? mceP->size : size + offset;
231
232     MReleaseWriteLock(&mceP->afs_memLock);
233     return size;
234 }
235
236 /*XXX: this extends a block arbitrarily to support big directories */
237 int
238 afs_MemWritevBlk(register struct memCacheEntry *mceP, int offset,
239                  struct iovec *iov, int nio, int size)
240 {
241     int i;
242     int bytesWritten;
243     int bytesToWrite;
244     AFS_STATCNT(afs_MemWriteBlk);
245     MObtainWriteLock(&mceP->afs_memLock, 561);
246     if (offset + size > mceP->dataSize) {
247         char *oldData = mceP->data;
248
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",
254                      size + offset);
255             return -ENOMEM;
256         }
257
258         /* may overlap, but this is OK */
259         AFS_GUNLOCK();
260         memcpy(mceP->data, oldData, mceP->size);
261         AFS_GLOCK();
262         afs_osi_Free(oldData, mceP->dataSize);
263         mceP->dataSize = size + offset;
264     }
265     AFS_GUNLOCK();
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;
274     }
275     mceP->size = (offset < mceP->size) ? mceP->size : offset;
276     AFS_GLOCK();
277
278     MReleaseWriteLock(&mceP->afs_memLock);
279     return bytesWritten;
280 }
281
282 int
283 afs_MemWriteUIO(ino_t blkno, struct uio *uioP)
284 {
285     register struct memCacheEntry *mceP =
286         (struct memCacheEntry *)afs_MemCacheOpen(blkno);
287     afs_int32 code;
288
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;
293
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));
300             return -ENOMEM;
301         }
302
303         AFS_GUNLOCK();
304         memcpy(mceP->data, oldData, mceP->size);
305         AFS_GLOCK();
306
307         afs_osi_Free(oldData, mceP->dataSize);
308         mceP->dataSize = AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP);
309     }
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,
314                 uioP, code);
315     if (AFS_UIO_OFFSET(uioP) > mceP->size)
316         mceP->size = AFS_UIO_OFFSET(uioP);
317
318     MReleaseWriteLock(&mceP->afs_memLock);
319     return code;
320 }
321
322 int
323 afs_MemCacheTruncate(register struct osi_file *fP, int size)
324 {
325     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
326     AFS_STATCNT(afs_MemCacheTruncate);
327
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",
337                      memCacheBlkSize);
338         } else {
339             afs_osi_Free(oldData, mceP->dataSize);
340             mceP->dataSize = memCacheBlkSize;
341         }
342     }
343
344     if (size < mceP->size)
345         mceP->size = size;
346
347     MReleaseWriteLock(&mceP->afs_memLock);
348     return 0;
349 }
350
351 int
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)
357 {
358     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
359
360     register afs_int32 code;
361     register int tlen;
362     int offset = 0;
363     struct iovec *tiov;         /* no data copying with iovec */
364     int tnio;                   /* temp for iovec size */
365
366     AFS_STATCNT(afs_MemCacheStoreProc);
367 #ifndef AFS_NOSTATS
368     /*
369      * In this case, alen is *always* the amount of data we'll be trying
370      * to ship here.
371      */
372     *(abytesToXferP) = alen;
373     *(abytesXferredP) = 0;
374 #endif /* AFS_NOSTATS */
375
376     /* 
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
379      */
380     tiov =
381         (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec) *
382                                             RX_MAXIOVECS);
383     if (!tiov) {
384         osi_Panic
385             ("afs_MemCacheStoreProc: osi_AllocSmallSpace for iovecs returned NULL\n");
386     }
387 #ifdef notdef
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 */
393     if (shouldWake) {
394         if (AFS_CHUNK(tlen) != 0)
395             *shouldWake = 0;
396         else
397             *shouldWake = 1;
398     }
399 #endif /* notdef */
400
401     while (alen > 0) {
402         tlen = (alen > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : alen);
403         RX_AFS_GUNLOCK();
404         code = rx_WritevAlloc(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
405         RX_AFS_GLOCK();
406         if (code <= 0) {
407             code = rx_Error(acall);
408             osi_FreeSmallSpace(tiov);
409             return code ? code : -33;
410         }
411         tlen = code;
412         code = afs_MemReadvBlk(mceP, offset, tiov, tnio, tlen);
413         if (code != tlen) {
414             osi_FreeSmallSpace(tiov);
415             return -33;
416         }
417         RX_AFS_GUNLOCK();
418         code = rx_Writev(acall, tiov, tnio, tlen);
419         RX_AFS_GLOCK();
420 #ifndef AFS_NOSTATS
421         (*abytesXferredP) += code;
422 #endif /* AFS_NOSTATS */
423         if (code != tlen) {
424             code = rx_Error(acall);
425             osi_FreeSmallSpace(tiov);
426             return code ? code : -33;
427         }
428         offset += tlen;
429         alen -= tlen;
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 */
433             afs_wakeup(avc);
434         }
435     }
436     osi_FreeSmallSpace(tiov);
437     return 0;
438 }
439
440 int
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)
446 {
447     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
448     register afs_int32 code;
449     afs_int32 length;
450     int moredata = 0;
451     struct iovec *tiov;         /* no data copying with iovec */
452     register int tlen, offset = 0;
453     int tnio;                   /* temp for iovec size */
454
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);
460 #ifndef AFS_NOSTATS
461     (*abytesToXferP) = 0;
462     (*abytesXferredP) = 0;
463 #endif /* AFS_NOSTATS */
464     /* 
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
467      */
468     tiov =
469         (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec) *
470                                             RX_MAXIOVECS);
471     if (!tiov) {
472         osi_Panic
473             ("afs_MemCacheFetchProc: osi_AllocSmallSpace for iovecs returned NULL\n");
474     }
475     adc->validPos = abase;
476     do {
477         if (moredata) {
478             RX_AFS_GUNLOCK();
479             code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
480             length = ntohl(length);
481             RX_AFS_GLOCK();
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 */
486             }
487         }
488         /*
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
492          * blocks expected.
493          *
494          * We do not do this for AFS file servers because they sometimes
495          * return large negative numbers as the transfer size.
496          */
497         if (avc->states & CForeign) {
498             moredata = length & 0x80000000;
499             length &= ~0x80000000;
500         } else {
501             moredata = 0;
502         }
503 #ifndef AFS_NOSTATS
504         (*abytesToXferP) += length;
505 #endif /* AFS_NOSTATS */
506         while (length > 0) {
507             tlen = (length > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : length);
508             RX_AFS_GUNLOCK();
509             code = rx_Readv(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
510             RX_AFS_GLOCK();
511 #ifndef AFS_NOSTATS
512             (*abytesXferredP) += code;
513 #endif /* AFS_NOSTATS */
514             if (code <= 0) {
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);
519                 return -34;
520             }
521             tlen = code;
522             afs_MemWritevBlk(mceP, offset, tiov, tnio, tlen);
523             offset += tlen;
524             abase += tlen;
525             length -= 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,
531                            adc->dflags);
532         }
533     } while (moredata);
534     /* max of two sizes */
535     osi_FreeSmallSpace(tiov);
536     return 0;
537 }
538
539
540 void
541 shutdown_memcache(void)
542 {
543     register int index;
544
545     if (cacheDiskType != AFS_FCACHE_TYPE_MEM)
546         return;
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);
551     }
552     afs_osi_Free((char *)memCache,
553                  memMaxBlkNumber * sizeof(struct memCacheEntry));
554     memMaxBlkNumber = 0;
555 }