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