5f29a03d36df7b7d27915dfbe8895a0d3faacb0e
[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
14 #include "afs/sysincludes.h"    /* Standard vendor system headers */
15 #ifndef AFS_LINUX22_ENV
16 #include "rpc/types.h"
17 #endif
18 #include "afsincludes.h"        /* Afs-based standard headers */
19 #include "afs/afs_stats.h"      /* statistics */
20
21 /* memory cache routines */
22 static struct memCacheEntry *memCache;
23 static int memCacheBlkSize = 8192;
24 static int memMaxBlkNumber = 0;
25
26 extern int cacheDiskType;
27
28 int
29 afs_InitMemCache(int blkCount, int blkSize, int flags)
30 {
31     int index;
32
33     AFS_STATCNT(afs_InitMemCache);
34     if (blkSize)
35         memCacheBlkSize = blkSize;
36
37     memMaxBlkNumber = blkCount;
38     memCache =
39         afs_osi_Alloc(memMaxBlkNumber * sizeof(struct memCacheEntry));
40     osi_Assert(memCache != NULL);
41
42     for (index = 0; index < memMaxBlkNumber; index++) {
43         char *blk;
44         (memCache + index)->size = 0;
45         (memCache + index)->dataSize = memCacheBlkSize;
46         LOCK_INIT(&((memCache + index)->afs_memLock), "afs_memLock");
47         blk = afs_osi_Alloc(memCacheBlkSize);
48         if (blk == NULL)
49             goto nomem;
50         (memCache + index)->data = blk;
51         memset((memCache + index)->data, 0, memCacheBlkSize);
52     }
53 #if defined(AFS_HAVE_VXFS)
54     afs_InitDualFSCacheOps((struct vnode *)0);
55 #endif
56
57     return 0;
58
59   nomem:
60     afs_warn("afsd:  memCache allocation failure at %d KB.\n",
61              (index * memCacheBlkSize) / 1024);
62     while (--index >= 0) {
63         afs_osi_Free((memCache + index)->data, memCacheBlkSize);
64         (memCache + index)->data = NULL;
65     }
66     return ENOMEM;
67
68 }
69
70 int
71 afs_MemCacheClose(struct osi_file *file)
72 {
73     return 0;
74 }
75
76 void *
77 afs_MemCacheOpen(afs_dcache_id_t *ainode)
78 {
79     struct memCacheEntry *mep;
80
81     if (ainode->mem < 0 || ainode->mem > memMaxBlkNumber) {
82         osi_Panic("afs_MemCacheOpen: invalid block #");
83     }
84     mep = (memCache + ainode->mem);
85     afs_Trace3(afs_iclSetp, CM_TRACE_MEMOPEN, ICL_TYPE_INT32, ainode->mem,
86                ICL_TYPE_POINTER, mep, ICL_TYPE_POINTER, mep ? mep->data : 0);
87     return (void *)mep;
88 }
89
90 /*
91  * this routine simulates a read in the Memory Cache
92  */
93 int
94 afs_MemReadBlk(struct osi_file *fP, int offset, void *dest,
95                int size)
96 {
97     struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
98     int bytesRead;
99
100     ObtainReadLock(&mceP->afs_memLock);
101     AFS_STATCNT(afs_MemReadBlk);
102     if (offset < 0) {
103         ReleaseReadLock(&mceP->afs_memLock);
104         return 0;
105     }
106     /* use min of bytes in buffer or requested size */
107     bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
108
109     if (bytesRead > 0) {
110         AFS_GUNLOCK();
111         memcpy(dest, mceP->data + offset, bytesRead);
112         AFS_GLOCK();
113     } else
114         bytesRead = 0;
115
116     ReleaseReadLock(&mceP->afs_memLock);
117     return bytesRead;
118 }
119
120 /*
121  * this routine simulates a readv in the Memory Cache
122  */
123 int
124 afs_MemReadvBlk(struct memCacheEntry *mceP, int offset,
125                 struct iovec *iov, int nio, int size)
126 {
127     int i;
128     int bytesRead;
129     int bytesToRead;
130
131     ObtainReadLock(&mceP->afs_memLock);
132     AFS_STATCNT(afs_MemReadBlk);
133     if (offset < 0) {
134         ReleaseReadLock(&mceP->afs_memLock);
135         return 0;
136     }
137     /* use min of bytes in buffer or requested size */
138     bytesRead = (size < mceP->size - offset) ? size : mceP->size - offset;
139
140     if (bytesRead > 0) {
141         for (i = 0, size = bytesRead; i < nio && size > 0; i++) {
142             bytesToRead = (size < iov[i].iov_len) ? size : iov[i].iov_len;
143             AFS_GUNLOCK();
144             memcpy(iov[i].iov_base, mceP->data + offset, bytesToRead);
145             AFS_GLOCK();
146             offset += bytesToRead;
147             size -= bytesToRead;
148         }
149         bytesRead -= size;
150     } else
151         bytesRead = 0;
152
153     ReleaseReadLock(&mceP->afs_memLock);
154     return bytesRead;
155 }
156
157 int
158 afs_MemReadUIO(afs_dcache_id_t *ainode, struct uio *uioP)
159 {
160     struct memCacheEntry *mceP =
161         (struct memCacheEntry *)afs_MemCacheOpen(ainode);
162     int length = mceP->size - AFS_UIO_OFFSET(uioP);
163     afs_int32 code;
164
165     AFS_STATCNT(afs_MemReadUIO);
166     ObtainReadLock(&mceP->afs_memLock);
167     length = (length < AFS_UIO_RESID(uioP)) ? length : AFS_UIO_RESID(uioP);
168     AFS_UIOMOVE(mceP->data + AFS_UIO_OFFSET(uioP), length, UIO_READ, uioP, code);
169     ReleaseReadLock(&mceP->afs_memLock);
170     return code;
171 }
172
173 int
174 afs_MemWriteBlk(struct osi_file *fP, int offset, void *src,
175                 int size)
176 {
177     struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
178     struct iovec tiov;
179
180     tiov.iov_base = src;
181     tiov.iov_len = size;
182     return afs_MemWritevBlk(mceP, offset, &tiov, 1, size);
183 }
184
185 /*XXX: this extends a block arbitrarily to support big directories */
186 int
187 afs_MemWritevBlk(struct memCacheEntry *mceP, int offset,
188                  struct iovec *iov, int nio, int size)
189 {
190     int i;
191     int bytesWritten;
192     int bytesToWrite;
193     AFS_STATCNT(afs_MemWriteBlk);
194     ObtainWriteLock(&mceP->afs_memLock, 561);
195     if (offset + size > mceP->dataSize) {
196         char *oldData = mceP->data;
197
198         mceP->data = afs_osi_Alloc(size + offset);
199         if (mceP->data == NULL) {       /* no available memory */
200             mceP->data = oldData;       /* revert back change that was made */
201             ReleaseWriteLock(&mceP->afs_memLock);
202             afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
203                      size + offset);
204             return -ENOMEM;
205         }
206
207         /* may overlap, but this is OK */
208         AFS_GUNLOCK();
209         memcpy(mceP->data, oldData, mceP->size);
210         AFS_GLOCK();
211         afs_osi_Free(oldData, mceP->dataSize);
212         mceP->dataSize = size + offset;
213     }
214     AFS_GUNLOCK();
215     if (mceP->size < offset)
216         memset(mceP->data + mceP->size, 0, offset - mceP->size);
217     for (bytesWritten = 0, i = 0; i < nio && size > 0; i++) {
218         bytesToWrite = (size < iov[i].iov_len) ? size : iov[i].iov_len;
219         memcpy(mceP->data + offset, iov[i].iov_base, bytesToWrite);
220         offset += bytesToWrite;
221         bytesWritten += bytesToWrite;
222         size -= bytesToWrite;
223     }
224     mceP->size = (offset < mceP->size) ? mceP->size : offset;
225     AFS_GLOCK();
226
227     ReleaseWriteLock(&mceP->afs_memLock);
228     return bytesWritten;
229 }
230
231 int
232 afs_MemWriteUIO(struct vcache *avc, afs_dcache_id_t *ainode, struct uio *uioP)
233 {
234     struct memCacheEntry *mceP =
235         (struct memCacheEntry *)afs_MemCacheOpen(ainode);
236     afs_int32 code;
237
238     AFS_STATCNT(afs_MemWriteUIO);
239     ObtainWriteLock(&mceP->afs_memLock, 312);
240     if (AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP) > mceP->dataSize) {
241         char *oldData = mceP->data;
242
243         mceP->data = afs_osi_Alloc(AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP));
244         if (mceP->data == NULL) {       /* no available memory */
245             mceP->data = oldData;       /* revert back change that was made */
246             ReleaseWriteLock(&mceP->afs_memLock);
247             afs_warn("afs: afs_MemWriteBlk mem alloc failure (%ld bytes)\n",
248                      (long)(AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP)));
249             return -ENOMEM;
250         }
251
252         AFS_GUNLOCK();
253         memcpy(mceP->data, oldData, mceP->size);
254         AFS_GLOCK();
255
256         afs_osi_Free(oldData, mceP->dataSize);
257         mceP->dataSize = AFS_UIO_RESID(uioP) + AFS_UIO_OFFSET(uioP);
258     }
259     if (mceP->size < AFS_UIO_OFFSET(uioP))
260         memset(mceP->data + mceP->size, 0,
261                (int)(AFS_UIO_OFFSET(uioP) - mceP->size));
262     AFS_UIOMOVE(mceP->data + AFS_UIO_OFFSET(uioP), AFS_UIO_RESID(uioP), UIO_WRITE,
263                 uioP, code);
264     if (AFS_UIO_OFFSET(uioP) > mceP->size)
265         mceP->size = AFS_UIO_OFFSET(uioP);
266
267     ReleaseWriteLock(&mceP->afs_memLock);
268     return code;
269 }
270
271 int
272 afs_MemCacheTruncate(struct osi_file *fP, int size)
273 {
274     struct memCacheEntry *mceP = (struct memCacheEntry *)fP;
275     AFS_STATCNT(afs_MemCacheTruncate);
276
277     ObtainWriteLock(&mceP->afs_memLock, 313);
278     /* old directory entry; g.c. */
279     if (size == 0 && mceP->dataSize > memCacheBlkSize) {
280         char *oldData = mceP->data;
281         mceP->data = afs_osi_Alloc(memCacheBlkSize);
282         if (mceP->data == NULL) {       /* no available memory */
283             mceP->data = oldData;
284             ReleaseWriteLock(&mceP->afs_memLock);
285             afs_warn("afs: afs_MemWriteBlk mem alloc failure (%d bytes)\n",
286                      memCacheBlkSize);
287         } else {
288             afs_osi_Free(oldData, mceP->dataSize);
289             mceP->dataSize = memCacheBlkSize;
290         }
291     }
292
293     if (size < mceP->size)
294         mceP->size = size;
295
296     ReleaseWriteLock(&mceP->afs_memLock);
297     return 0;
298 }
299
300
301 void
302 shutdown_memcache(void)
303 {
304     int index;
305
306     if (cacheDiskType != AFS_FCACHE_TYPE_MEM)
307         return;
308     memCacheBlkSize = 8192;
309     for (index = 0; index < memMaxBlkNumber; index++) {
310         LOCK_INIT(&((memCache + index)->afs_memLock), "afs_memLock");
311         afs_osi_Free((memCache + index)->data, (memCache + index)->dataSize);
312     }
313     afs_osi_Free((char *)memCache,
314                  memMaxBlkNumber * sizeof(struct memCacheEntry));
315     memMaxBlkNumber = 0;
316 }