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