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