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