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