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