windows-dirty-buffer-optimization-20070808
[openafs.git] / src / WINNT / afsd / rawops.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 <osi.h>
11 #include "afsd.h"
12
13 #include "afsdifs.h"
14
15 #define CM_BUF_SIZE             4096
16 long buf_bufferSize = CM_BUF_SIZE;
17
18 long ReadData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
19               cm_user_t *userp, long *readp)
20 {
21     //osi_hyper_t offset;
22     long code;
23     cm_buf_t *bufferp;
24     osi_hyper_t fileLength;
25     osi_hyper_t thyper;
26     osi_hyper_t lastByte;
27     osi_hyper_t bufferOffset;
28     long bufIndex, nbytes;
29     int sequential = 0;
30     cm_req_t req;
31
32     cm_InitReq(&req);
33
34     bufferp = NULL;
35
36     lock_ObtainMutex(&scp->mx);
37
38     /* start by looking up the file's end */
39     code = cm_SyncOp(scp, NULL, userp, &req, 0,
40                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
41     if (code) 
42         goto done;
43
44     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
45
46     /* now we have the entry locked, look up the length */
47     fileLength = scp->length;
48
49     /* adjust count down so that it won't go past EOF */
50     thyper.LowPart = count;
51     thyper.HighPart = 0;
52     thyper = LargeIntegerAdd(offset, thyper);   /* where read should end */
53     lastByte = thyper;
54     if (LargeIntegerGreaterThan(thyper, fileLength)) {
55         /* we'd read past EOF, so just stop at fileLength bytes.
56         * Start by computing how many bytes remain in the file.
57         */
58         thyper = LargeIntegerSubtract(fileLength, offset);
59
60         /* if we are past EOF, read 0 bytes */
61         if (LargeIntegerLessThanZero(thyper))
62             count = 0;
63         else
64             count = thyper.LowPart;
65     }       
66
67     *readp = count;
68
69     /* now, copy the data one buffer at a time,
70      * until we've filled the request packet
71      */
72     while (1) {
73         /* if we've copied all the data requested, we're done */
74         if (count <= 0) break;
75
76         /* otherwise, load up a buffer of data */
77         thyper.HighPart = offset.HighPart;
78         thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
79         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
80             /* wrong buffer */
81             if (bufferp) {
82                 buf_Release(bufferp);
83                 bufferp = NULL;
84             }
85             lock_ReleaseMutex(&scp->mx);
86
87             lock_ObtainRead(&scp->bufCreateLock);
88             code = buf_Get(scp, &thyper, &bufferp);
89             lock_ReleaseRead(&scp->bufCreateLock);
90
91             lock_ObtainMutex(&scp->mx);
92             if (code) goto done;
93             bufferOffset = thyper;
94
95             /* now get the data in the cache */
96             while (1) {
97                 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
98                                   CM_SCACHESYNC_NEEDCALLBACK
99                                   | CM_SCACHESYNC_READ);
100                 if (code) 
101                     goto done;
102
103                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
104
105                 if (cm_HaveBuffer(scp, bufferp, 0)) break;
106
107                 /* otherwise, load the buffer and try again */
108                 code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
109                 if (code) break;
110             }
111             if (code) {
112                 buf_Release(bufferp);
113                 bufferp = NULL;
114                 goto done;
115             }
116         }       /* if (wrong buffer) ... */
117
118         /* now we have the right buffer loaded.  Copy out the
119          * data from here to the user's buffer.
120          */
121         bufIndex = offset.LowPart & (buf_bufferSize - 1);
122
123         /* and figure out how many bytes we want from this buffer */
124         nbytes = buf_bufferSize - bufIndex;     /* what remains in buffer */
125         if (nbytes > count) nbytes = count;     /* don't go past EOF */
126
127         /* now copy the data */
128         memcpy(op, bufferp->datap + bufIndex, nbytes);
129
130         /* adjust counters, pointers, etc. */
131         op += nbytes;
132         count -= nbytes;
133         thyper.LowPart = nbytes;
134         thyper.HighPart = 0;
135         offset = LargeIntegerAdd(thyper, offset);
136     } /* while 1 */
137
138   done:
139     lock_ReleaseMutex(&scp->mx);
140     //lock_ReleaseMutex(&fidp->mx);
141     if (bufferp) 
142         buf_Release(bufferp);
143
144     if (code == 0 && sequential)
145         cm_ConsiderPrefetch(scp, &lastByte, userp, &req);
146
147     return code;
148 }       
149
150
151 long WriteData(cm_scache_t *scp, osi_hyper_t offset, long count, char *op,
152                cm_user_t *userp, long *writtenp)
153 {
154     long code = 0;
155     long written = 0;
156     osi_hyper_t fileLength;     /* file's length at start of write */
157     osi_hyper_t minLength;      /* don't read past this */
158     afs_uint32 nbytes;          /* # of bytes to transfer this iteration */
159     cm_buf_t *bufferp;
160     osi_hyper_t thyper;         /* hyper tmp variable */
161     osi_hyper_t bufferOffset;
162     afs_uint32 bufIndex;        /* index in buffer where our data is */
163     int doWriteBack;
164     osi_hyper_t writeBackOffset;        /* offset of region to write back when
165     * I/O is done */
166     DWORD filter = 0;
167     cm_req_t req;
168
169     cm_InitReq(&req);
170
171     bufferp = NULL;
172     doWriteBack = 0;
173
174     lock_ObtainMutex(&scp->mx);
175
176     /* start by looking up the file's end */
177     code = cm_SyncOp(scp, NULL, userp, &req, 0,
178                      CM_SCACHESYNC_NEEDCALLBACK
179                       | CM_SCACHESYNC_SETSTATUS
180                       | CM_SCACHESYNC_GETSTATUS);
181     if (code) 
182         goto done;
183     
184     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_GETSTATUS);
185
186 #if 0
187     /* make sure we have a writable FD */
188     if (!(fidp->flags & SMB_FID_OPENWRITE)) {
189     code = CM_ERROR_BADFDOP;
190     goto done;
191     }
192 #endif
193         
194     /* now we have the entry locked, look up the length */
195     fileLength = scp->length;
196     minLength = fileLength;
197     if (LargeIntegerGreaterThan(minLength, scp->serverLength))
198         minLength = scp->serverLength;
199
200     /* adjust file length if we extend past EOF */
201     thyper.LowPart = count;
202     thyper.HighPart = 0;
203     thyper = LargeIntegerAdd(offset, thyper);   /* where write should end */
204     if (LargeIntegerGreaterThan(thyper, fileLength)) {
205         /* we'd write past EOF, so extend the file */
206         scp->mask |= CM_SCACHEMASK_LENGTH;
207         scp->length = thyper;
208         filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
209     } else
210         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
211
212     /* now, if the new position (thyper) and the old (offset) are in
213      * different storeback windows, remember to store back the previous
214      * storeback window when we're done with the write.
215      */
216     if ((thyper.LowPart & (-cm_chunkSize)) !=
217          (offset.LowPart & (-cm_chunkSize))) {
218         /* they're different */
219         doWriteBack = 1;
220         writeBackOffset.HighPart = offset.HighPart;
221         writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize);
222     }
223
224     *writtenp = count;
225
226     /* now, copy the data one buffer at a time, until we've filled the
227      * request packet */
228     while (1) {
229         /* if we've copied all the data requested, we're done */
230         if (count <= 0) break;
231
232         /* handle over quota or out of space */
233         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
234             *writtenp = written;
235             break;
236         }
237
238         /* otherwise, load up a buffer of data */
239         thyper.HighPart = offset.HighPart;
240         thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
241         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
242             /* wrong buffer */
243             if (bufferp) {
244                 lock_ReleaseMutex(&bufferp->mx);
245                 buf_Release(bufferp);
246                 bufferp = NULL;
247             }   
248             lock_ReleaseMutex(&scp->mx);
249
250             lock_ObtainRead(&scp->bufCreateLock);
251             code = buf_Get(scp, &thyper, &bufferp);
252             lock_ReleaseRead(&scp->bufCreateLock);
253
254             lock_ObtainMutex(&bufferp->mx);
255             lock_ObtainMutex(&scp->mx);
256             if (code) 
257                 goto done;
258
259             bufferOffset = thyper;
260
261             /* now get the data in the cache */
262             while (1) {
263                 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
264                                   CM_SCACHESYNC_NEEDCALLBACK
265                                   | CM_SCACHESYNC_WRITE
266                                   | CM_SCACHESYNC_BUFLOCKED);
267                 if (code) 
268                     goto done;
269                        
270                 cm_SyncOpDone(scp, bufferp, 
271                                CM_SCACHESYNC_NEEDCALLBACK 
272                                | CM_SCACHESYNC_WRITE 
273                                | CM_SCACHESYNC_BUFLOCKED);
274
275                 /* If we're overwriting the entire buffer, or
276                  * if we're writing at or past EOF, mark the
277                  * buffer as current so we don't call
278                  * cm_GetBuffer.  This skips the fetch from the
279                  * server in those cases where we're going to 
280                  * obliterate all the data in the buffer anyway,
281                  * or in those cases where there is no useful
282                  * data at the server to start with.
283                  *
284                  * Use minLength instead of scp->length, since
285                  * the latter has already been updated by this
286                  * call.
287                  */
288                 if (LargeIntegerGreaterThanOrEqualTo(bufferp->offset, minLength) ||
289                      LargeIntegerEqualTo(offset, bufferp->offset) &&
290                      (count >= buf_bufferSize ||
291                        LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(offset, ConvertLongToLargeInteger(count)), minLength))) {
292                     if (count < buf_bufferSize
293                          && bufferp->dataVersion == -1)
294                         memset(bufferp->datap, 0,
295                                 buf_bufferSize);
296                     bufferp->dataVersion = scp->dataVersion;
297                 }
298
299                 if (cm_HaveBuffer(scp, bufferp, 1)) break;
300
301                 /* otherwise, load the buffer and try again */
302                 lock_ReleaseMutex(&bufferp->mx);
303                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
304                                      &req);
305                 lock_ReleaseMutex(&scp->mx);
306                 lock_ObtainMutex(&bufferp->mx);
307                 lock_ObtainMutex(&scp->mx);
308                 if (code) 
309                     break;
310             }
311             if (code) {
312                 lock_ReleaseMutex(&bufferp->mx);
313                 buf_Release(bufferp);
314                 bufferp = NULL;
315                 goto done;
316             }
317         }       /* if (wrong buffer) ... */
318
319         /* now we have the right buffer loaded.  Copy out the
320          * data from here to the user's buffer.
321          */
322         bufIndex = offset.LowPart & (buf_bufferSize - 1);
323
324         /* and figure out how many bytes we want from this buffer */
325         nbytes = buf_bufferSize - bufIndex;     /* what remains in buffer */
326         if (nbytes > count) 
327             nbytes = count;     /* don't go past end of request */
328
329         /* now copy the data */
330         memcpy(bufferp->datap + bufIndex, op, nbytes);
331         buf_SetDirty(bufferp, bufIndex, nbytes);
332
333         /* and record the last writer */
334         if (bufferp->userp != userp) {
335             cm_HoldUser(userp);
336             if (bufferp->userp) 
337                 cm_ReleaseUser(bufferp->userp);
338             bufferp->userp = userp;
339         }
340
341         /* adjust counters, pointers, etc. */
342         op += nbytes;
343         count -= nbytes;
344         written += nbytes;
345         thyper.LowPart = nbytes;
346         thyper.HighPart = 0;
347         offset = LargeIntegerAdd(thyper, offset);
348     } /* while 1 */
349
350   done:
351     lock_ReleaseMutex(&scp->mx);
352     if (bufferp) {
353         lock_ReleaseMutex(&bufferp->mx);
354         buf_Release(bufferp);
355     }
356
357     if (code == 0 && doWriteBack) {
358         lock_ObtainMutex(&scp->mx);
359         cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
360         lock_ReleaseMutex(&scp->mx);
361         cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
362                             writeBackOffset.HighPart, cm_chunkSize, 0, userp);
363     }   
364
365     /* cm_SyncOpDone is called when cm_BkgStore completes */
366     return code;
367 }
368
369