amd64-linux-dewarn-20041202
[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_ALPHA_ENV
21 #undef kmem_alloc
22 #undef kmem_free
23 #undef mem_alloc
24 #undef mem_free
25 #undef register
26 #endif /* AFS_ALPHA_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             osi_FreeSmallSpace(tiov);
387             return -33;
388         }
389         tlen = code;
390         code = afs_MemReadvBlk(mceP, offset, tiov, tnio, tlen);
391         if (code != tlen) {
392             osi_FreeSmallSpace(tiov);
393             return -33;
394         }
395         RX_AFS_GUNLOCK();
396         code = rx_Writev(acall, tiov, tnio, tlen);
397         RX_AFS_GLOCK();
398 #ifndef AFS_NOSTATS
399         (*abytesXferredP) += code;
400 #endif /* AFS_NOSTATS */
401         if (code != tlen) {
402             osi_FreeSmallSpace(tiov);
403             return -33;
404         }
405         offset += tlen;
406         alen -= tlen;
407         /* if file has been locked on server, can allow store to continue */
408         if (shouldWake && *shouldWake && (rx_GetRemoteStatus(acall) & 1)) {
409             *shouldWake = 0;    /* only do this once */
410             afs_wakeup(avc);
411         }
412     }
413     osi_FreeSmallSpace(tiov);
414     return 0;
415 }
416
417 int
418 afs_MemCacheFetchProc(register struct rx_call *acall,
419                       register struct osi_file *fP, afs_size_t abase,
420                       struct dcache *adc, struct vcache *avc,
421                       afs_size_t * abytesToXferP, afs_size_t * abytesXferredP,
422                       afs_int32 lengthFound)
423 {
424     register struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
425     register afs_int32 code;
426     afs_int32 length;
427     int moredata = 0;
428     struct iovec *tiov;         /* no data copying with iovec */
429     register int tlen, offset = 0;
430     int tnio;                   /* temp for iovec size */
431
432     AFS_STATCNT(afs_MemCacheFetchProc);
433     length = lengthFound;
434     afs_Trace4(afs_iclSetp, CM_TRACE_MEMFETCH, ICL_TYPE_POINTER, avc,
435                ICL_TYPE_POINTER, mceP, ICL_TYPE_OFFSET,
436                ICL_HANDLE_OFFSET(abase), ICL_TYPE_INT32, length);
437 #ifndef AFS_NOSTATS
438     (*abytesToXferP) = 0;
439     (*abytesXferredP) = 0;
440 #endif /* AFS_NOSTATS */
441     /* 
442      * We need to alloc the iovecs on the heap so that they are "pinned" rather than
443      * declare them on the stack - defect 11272
444      */
445     tiov =
446         (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec) *
447                                             RX_MAXIOVECS);
448     if (!tiov) {
449         osi_Panic
450             ("afs_MemCacheFetchProc: osi_AllocSmallSpace for iovecs returned NULL\n");
451     }
452     adc->validPos = abase;
453     do {
454         if (moredata) {
455             RX_AFS_GUNLOCK();
456             code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
457             length = ntohl(length);
458             RX_AFS_GLOCK();
459             if (code != sizeof(afs_int32)) {
460                 code = rx_Error(acall);
461                 osi_FreeSmallSpace(tiov);
462                 return (code ? code : -1);      /* try to return code, not -1 */
463             }
464         }
465         /*
466          * The fetch protocol is extended for the AFS/DFS translator
467          * to allow multiple blocks of data, each with its own length,
468          * to be returned. As long as the top bit is set, there are more
469          * blocks expected.
470          *
471          * We do not do this for AFS file servers because they sometimes
472          * return large negative numbers as the transfer size.
473          */
474         if (avc->states & CForeign) {
475             moredata = length & 0x80000000;
476             length &= ~0x80000000;
477         } else {
478             moredata = 0;
479         }
480 #ifndef AFS_NOSTATS
481         (*abytesToXferP) += length;
482 #endif /* AFS_NOSTATS */
483         while (length > 0) {
484             tlen = (length > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : length);
485             RX_AFS_GUNLOCK();
486             code = rx_Readv(acall, tiov, &tnio, RX_MAXIOVECS, tlen);
487             RX_AFS_GLOCK();
488 #ifndef AFS_NOSTATS
489             (*abytesXferredP) += code;
490 #endif /* AFS_NOSTATS */
491             if (code <= 0) {
492                 afs_Trace3(afs_iclSetp, CM_TRACE_FETCH64READ,
493                            ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code,
494                            ICL_TYPE_INT32, length);
495                 osi_FreeSmallSpace(tiov);
496                 return -34;
497             }
498             tlen = code;
499             afs_MemWritevBlk(mceP, offset, tiov, tnio, tlen);
500             offset += tlen;
501             abase += tlen;
502             length -= tlen;
503             adc->validPos = abase;
504             if (afs_osi_Wakeup(&adc->validPos) == 0)
505                 afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAKE, ICL_TYPE_STRING,
506                            __FILE__, ICL_TYPE_INT32, __LINE__,
507                            ICL_TYPE_POINTER, adc, ICL_TYPE_INT32,
508                            adc->dflags);
509         }
510     } while (moredata);
511     /* max of two sizes */
512     osi_FreeSmallSpace(tiov);
513     return 0;
514 }
515
516
517 void
518 shutdown_memcache(void)
519 {
520     register int index;
521
522     if (cacheDiskType != AFS_FCACHE_TYPE_MEM)
523         return;
524     memCacheBlkSize = 8192;
525     for (index = 0; index < memMaxBlkNumber; index++) {
526         LOCK_INIT(&((memCache + index)->afs_memLock), "afs_memLock");
527         afs_osi_Free((memCache + index)->data, (memCache + index)->dataSize);
528     }
529     afs_osi_Free((char *)memCache,
530                  memMaxBlkNumber * sizeof(struct memCacheEntry));
531     memMaxBlkNumber = 0;
532 }