Cache bypass: release and unlock pages when we get 0-length reply
[openafs.git] / src / afs / afs_bypasscache.c
1 /*
2  * COPYRIGHT  ©  2000
3  * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
4  * ALL RIGHTS RESERVED
5  *
6  * Permission is granted to use, copy, create derivative works
7  * and redistribute this software and such derivative works
8  * for any purpose, so long as the name of The University of
9  * Michigan is not used in any advertising or publicity
10  * pertaining to the use of distribution of this software
11  * without specific, written prior authorization.  If the
12  * above copyright notice or any other identification of the
13  * University of Michigan is included in any copy of any
14  * portion of this software, then the disclaimer below must
15  * also be included.
16  *
17  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
18  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
19  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY O
20  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
21  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
22  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
23  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
25  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
26  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
27  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGES.
29  */
30
31  /*
32  * Portions Copyright (c) 2008
33  * The Linux Box Corporation
34  * ALL RIGHTS RESERVED
35  *
36  * Permission is granted to use, copy, create derivative works
37  * and redistribute this software and such derivative works
38  * for any purpose, so long as the name of the Linux Box
39  * Corporation is not used in any advertising or publicity
40  * pertaining to the use or distribution of this software
41  * without specific, written prior authorization.  If the
42  * above copyright notice or any other identification of the
43  * Linux Box Corporation is included in any copy of any
44  * portion of this software, then the disclaimer below must
45  * also be included.
46  *
47  * This software is provided as is, without representation
48  * from the Linux Box Corporation as to its fitness for any
49  * purpose, and without warranty by the Linux Box Corporation
50  * of any kind, either express or implied, including
51  * without limitation the implied warranties of
52  * merchantability and fitness for a particular purpose.  The
53  * Linux Box Corporation shall not be liable for any damages,
54  * including special, indirect, incidental, or consequential
55  * damages, with respect to any claim arising out of or in
56  * connection with the use of the software, even if it has been
57  * or is hereafter advised of the possibility of such damages.
58  */
59
60
61 #include <afsconfig.h>
62 #include "afs/param.h"
63
64 #if defined(AFS_CACHE_BYPASS)
65
66 #include "afs/afs_bypasscache.h"
67
68 /*
69  * afs_bypasscache.c
70  *
71  */
72 #include "afs/sysincludes.h" /* Standard vendor system headers */
73 #include "afs/afsincludes.h" /* Afs-based standard headers */
74 #include "afs/afs_stats.h"   /* statistics */
75 #include "afs/nfsclient.h"
76 #include "rx/rx_globals.h"
77
78 #if defined(AFS_LINUX26_ENV)
79 #define LockPage(pp) lock_page(pp)
80 #define UnlockPage(pp) unlock_page(pp)
81 #endif
82 #define AFS_KMAP_ATOMIC
83
84 #ifndef afs_min
85 #define afs_min(A,B) ((A)<(B)) ? (A) : (B)
86 #endif
87
88 /* conditional GLOCK macros */
89 #define COND_GLOCK(var) \
90         do { \
91                 var = ISAFS_GLOCK(); \
92                 if(!var) \
93                         RX_AFS_GLOCK(); \
94         } while(0)
95
96 #define COND_RE_GUNLOCK(var) \
97         do { \
98                 if(var) \
99                         RX_AFS_GUNLOCK(); \
100         } while(0)
101
102
103 /* conditional GUNLOCK macros */
104
105 #define COND_GUNLOCK(var) \
106         do {    \
107                 var = ISAFS_GLOCK(); \
108                 if(var) \
109                         RX_AFS_GUNLOCK(); \
110         } while(0)
111
112 #define COND_RE_GLOCK(var) \
113         do { \
114                 if(var) \
115                         RX_AFS_GLOCK(); \
116         } while(0)
117
118
119 int cache_bypass_strategy   =   NEVER_BYPASS_CACHE;
120 int cache_bypass_threshold  =   AFS_CACHE_BYPASS_DISABLED; /* file size > threshold triggers bypass */
121 int cache_bypass_prefetch = 1;  /* Should we do prefetching ? */
122
123 extern afs_rwlock_t afs_xcbhash;
124
125 /*
126  * This is almost exactly like the PFlush() routine in afs_pioctl.c,
127  * but that routine is static.  We are about to change a file from
128  * normal caching to bypass it's caching.  Therefore, we want to
129  * free up any cache space in use by the file, and throw out any
130  * existing VM pages for the file.  We keep track of the number of
131  * times we go back and forth from caching to bypass.
132  */
133 void
134 afs_TransitionToBypass(struct vcache *avc,
135                        afs_ucred_t *acred, int aflags)
136 {
137
138     afs_int32 code;
139     struct vrequest treq;
140     int setDesire = 0;
141     int setManual = 0;
142
143     if (!avc)
144         return;
145
146     if (aflags & TRANSChangeDesiredBit)
147         setDesire = 1;
148     if (aflags & TRANSSetManualBit)
149         setManual = 1;
150
151 #ifdef AFS_BOZONLOCK_ENV
152     afs_BozonLock(&avc->pvnLock, avc);  /* Since afs_TryToSmush will do a pvn_vptrunc */
153 #else
154     AFS_GLOCK();
155 #endif
156
157     ObtainWriteLock(&avc->lock, 925);
158     /*
159      * Someone may have beat us to doing the transition - we had no lock
160      * when we checked the flag earlier.  No cause to panic, just return.
161      */
162     if (avc->f.states & FCSBypass)
163         goto done;
164
165     /* If we never cached this, just change state */
166     if (setDesire && (!(avc->cachingStates & FCSBypass))) {
167         avc->f.states |= FCSBypass;
168         goto done;
169     }
170
171     /* cg2v, try to store any chunks not written 20071204 */
172     if (avc->execsOrWriters > 0) {
173         code = afs_InitReq(&treq, acred);
174         if (!code)
175             code = afs_StoreAllSegments(avc, &treq, AFS_SYNC | AFS_LASTSTORE);
176     }
177
178 #if 0
179     /* also cg2v, don't dequeue the callback */
180     ObtainWriteLock(&afs_xcbhash, 956);
181     afs_DequeueCallback(avc);
182     ReleaseWriteLock(&afs_xcbhash);
183 #endif
184     avc->f.states &= ~(CStatd | CDirty);      /* next reference will re-stat */
185     /* now find the disk cache entries */
186     afs_TryToSmush(avc, acred, 1);
187     osi_dnlc_purgedp(avc);
188     if (avc->linkData && !(avc->f.states & CCore)) {
189         afs_osi_Free(avc->linkData, strlen(avc->linkData) + 1);
190         avc->linkData = NULL;
191     }
192
193     avc->cachingStates |= FCSBypass;    /* Set the bypass flag */
194     if(setDesire)
195         avc->cachingStates |= FCSDesireBypass;
196     if(setManual)
197         avc->cachingStates |= FCSManuallySet;
198     avc->cachingTransitions++;
199
200 done:
201     ReleaseWriteLock(&avc->lock);
202 #ifdef AFS_BOZONLOCK_ENV
203     afs_BozonUnlock(&avc->pvnLock, avc);
204 #else
205     AFS_GUNLOCK();
206 #endif
207 }
208
209 /*
210  * This is almost exactly like the PFlush() routine in afs_pioctl.c,
211  * but that routine is static.  We are about to change a file from
212  * bypassing caching to normal caching.  Therefore, we want to
213  * throw out any existing VM pages for the file.  We keep track of
214  * the number of times we go back and forth from caching to bypass.
215  */
216 void
217 afs_TransitionToCaching(struct vcache *avc,
218                         afs_ucred_t *acred,
219                         int aflags)
220 {
221     int resetDesire = 0;
222     int setManual = 0;
223
224     if (!avc)
225         return;
226
227     if (aflags & TRANSChangeDesiredBit)
228         resetDesire = 1;
229     if (aflags & TRANSSetManualBit)
230         setManual = 1;
231
232 #ifdef AFS_BOZONLOCK_ENV
233     afs_BozonLock(&avc->pvnLock, avc);  /* Since afs_TryToSmush will do a pvn_vptrunc */
234 #else
235     AFS_GLOCK();
236 #endif
237     ObtainWriteLock(&avc->lock, 926);
238     /*
239      * Someone may have beat us to doing the transition - we had no lock
240      * when we checked the flag earlier.  No cause to panic, just return.
241      */
242     if (!(avc->f.states & FCSBypass))
243         goto done;
244
245     /* Ok, we actually do need to flush */
246     ObtainWriteLock(&afs_xcbhash, 957);
247     afs_DequeueCallback(avc);
248     avc->f.states &= ~(CStatd | CDirty);        /* next reference will re-stat cache entry */
249     ReleaseWriteLock(&afs_xcbhash);
250     /* now find the disk cache entries */
251     afs_TryToSmush(avc, acred, 1);
252     osi_dnlc_purgedp(avc);
253     if (avc->linkData && !(avc->f.states & CCore)) {
254         afs_osi_Free(avc->linkData, strlen(avc->linkData) + 1);
255         avc->linkData = NULL;
256     }
257
258     avc->cachingStates &= ~(FCSBypass);    /* Reset the bypass flag */
259     if (resetDesire)
260         avc->cachingStates &= ~(FCSDesireBypass);
261     if (setManual)
262         avc->cachingStates |= FCSManuallySet;
263     avc->cachingTransitions++;
264
265 done:
266     ReleaseWriteLock(&avc->lock);
267 #ifdef AFS_BOZONLOCK_ENV
268     afs_BozonUnlock(&avc->pvnLock, avc);
269 #else
270     AFS_GUNLOCK();
271 #endif
272 }
273
274 /* In the case where there's an error in afs_NoCacheFetchProc or
275  * afs_PrefetchNoCache, all of the pages they've been passed need
276  * to be unlocked.
277  */
278 #if defined(AFS_LINUX24_ENV)
279 #define unlock_and_release_pages(auio) \
280     do { \
281         struct iovec *ciov;     \
282         struct page *pp; \
283         afs_int32 iovmax; \
284         afs_int32 iovno = 0; \
285         ciov = auio->uio_iov; \
286         iovmax = auio->uio_iovcnt - 1;  \
287         pp = (struct page*) ciov->iov_base;     \
288         while(1) { \
289             if (pp) { \
290                 if (PageLocked(pp)) \
291                     UnlockPage(pp);     \
292                 put_page(pp); /* decrement refcount */ \
293             } \
294             iovno++; \
295             if(iovno > iovmax) \
296                 break; \
297             ciov = (auio->uio_iov + iovno);     \
298             pp = (struct page*) ciov->iov_base; \
299         } \
300     } while(0)
301 #else
302 #ifdef UKERNEL
303 #define unlock_and_release_pages(auio) \
304          do { } while(0)
305 #else
306 #error AFS_CACHE_BYPASS not implemented on this platform
307 #endif
308 #endif
309
310 /* no-cache prefetch routine */
311 static afs_int32
312 afs_NoCacheFetchProc(struct rx_call *acall,
313                      struct vcache *avc,
314                                          uio_t *auio,
315                      afs_int32 release_pages,
316                      afs_int32 size)
317 {
318     afs_int32 length;
319     afs_int32 code;
320     int tlen;
321     int moredata, iovno, iovoff, iovmax, clen, result, locked;
322     struct iovec *ciov;
323     struct page *pp;
324     char *address;
325 #ifdef AFS_KMAP_ATOMIC
326     char *page_buffer = osi_Alloc(PAGE_SIZE);
327 #else
328     char *page_buffer = NULL;
329 #endif
330
331     ciov = auio->uio_iov;
332     pp = (struct page*) ciov->iov_base;
333     iovmax = auio->uio_iovcnt - 1;
334     iovno = iovoff = result = 0;
335     do {
336
337         COND_GUNLOCK(locked);
338         code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
339         COND_RE_GLOCK(locked);
340
341         if (code != sizeof(afs_int32)) {
342             result = 0;
343             afs_warn("Preread error. code: %d instead of %d\n",
344                 code, (int)sizeof(afs_int32));
345             unlock_and_release_pages(auio);
346             goto done;
347         } else
348             length = ntohl(length);
349
350         if (length > size) {
351             result = EIO;
352             afs_warn("Preread error. Got length %d, which is greater than size %d\n",
353                      length, size);
354             unlock_and_release_pages(auio);
355             goto done;
356         }
357
358         /* If we get a 0 length reply, time to cleanup and return */
359         if (length == 0) {
360             unlock_and_release_pages(auio);
361             result = 0;
362             goto done;
363         }
364
365         /*
366          * The fetch protocol is extended for the AFS/DFS translator
367          * to allow multiple blocks of data, each with its own length,
368          * to be returned. As long as the top bit is set, there are more
369          * blocks expected.
370          *
371          * We do not do this for AFS file servers because they sometimes
372          * return large negative numbers as the transfer size.
373          */
374         if (avc->f.states & CForeign) {
375             moredata = length & 0x80000000;
376             length &= ~0x80000000;
377         } else {
378             moredata = 0;
379         }
380
381         while (length > 0) {
382
383             clen = ciov->iov_len - iovoff;
384             tlen = afs_min(length, clen);
385 #ifdef AFS_LINUX24_ENV
386 #ifndef AFS_KMAP_ATOMIC
387             if(pp)
388                 address = kmap(pp);
389             else {
390                 /* rx doesn't provide an interface to simply advance
391                    or consume n bytes.  for now, allocate a PAGE_SIZE
392                    region of memory to receive bytes in the case that
393                    there were holes in readpages */
394                 if(page_buffer == NULL)
395                     page_buffer = osi_Alloc(PAGE_SIZE);
396                     address = page_buffer;
397                 }
398 #else
399             address = page_buffer;
400 #endif
401 #else
402 #ifndef UKERNEL
403 #error AFS_CACHE_BYPASS not implemented on this platform
404 #endif
405 #endif /* LINUX24 */
406             COND_GUNLOCK(locked);
407             code = rx_Read(acall, address, tlen);
408             COND_RE_GLOCK(locked);
409
410             if (code < 0) {
411                 afs_warn("afs_NoCacheFetchProc: rx_Read error. Return code was %d\n", code);
412                 result = 0;
413                 unlock_and_release_pages(auio);
414                 goto done;
415             } else if (code == 0) {
416                 result = 0;
417                 afs_warn("afs_NoCacheFetchProc: rx_Read returned zero. Aborting.\n");
418                 unlock_and_release_pages(auio);
419                 goto done;
420             }
421             length -= code;
422             tlen -= code;
423
424             if(tlen > 0) {
425                 iovoff += code;
426                 address += code;
427             } else {
428 #ifdef AFS_LINUX24_ENV
429 #ifdef AFS_KMAP_ATOMIC
430                 if(pp) {
431                     address = kmap_atomic(pp, KM_USER0);
432                     memcpy(address, page_buffer, PAGE_SIZE);
433                     kunmap_atomic(address, KM_USER0);
434                 }
435 #endif
436 #else
437 #ifndef UKERNEL
438 #error AFS_CACHE_BYPASS not implemented on this platform
439 #endif
440 #endif /* LINUX 24 */
441                 /* we filled a page, conditionally release it */
442                 if (release_pages && ciov->iov_base) {
443                     /* this is appropriate when no caller intends to unlock
444                      * and release the page */
445 #ifdef AFS_LINUX24_ENV
446                     SetPageUptodate(pp);
447                     if(PageLocked(pp))
448                         UnlockPage(pp);
449                     else
450                         afs_warn("afs_NoCacheFetchProc: page not locked at iovno %d!\n", iovno);
451                     put_page(pp); /* decrement refcount */
452 #ifndef AFS_KMAP_ATOMIC
453                     kunmap(pp);
454 #endif
455 #else
456 #ifndef UKERNEL
457 #error AFS_CACHE_BYPASS not implemented on this platform
458 #endif
459 #endif /* LINUX24 */
460                 }
461                 /* and carry uio_iov */
462                 iovno++;
463                 if (iovno > iovmax)
464                     goto done;
465
466                 ciov = (auio->uio_iov + iovno);
467                 pp = (struct page*) ciov->iov_base;
468                 iovoff = 0;
469             }
470         }
471     } while (moredata);
472
473 done:
474     if(page_buffer)
475         osi_Free(page_buffer, PAGE_SIZE);
476     return result;
477 }
478
479
480 /* dispatch a no-cache read request */
481 afs_int32
482 afs_ReadNoCache(struct vcache *avc,
483                 struct nocache_read_request *bparms,
484                 afs_ucred_t *acred)
485 {
486     afs_int32 code;
487     afs_int32 bcnt;
488     struct brequest *breq;
489     struct vrequest *areq;
490
491     /* the reciever will free this */
492     areq = osi_Alloc(sizeof(struct vrequest));
493
494     if (avc && avc->vc_error) {
495         code = EIO;
496         afs_warn("afs_ReadNoCache VCache Error!\n");
497         goto cleanup;
498     }
499     if ((code = afs_InitReq(areq, acred))) {
500         afs_warn("afs_ReadNoCache afs_InitReq error!\n");
501         goto cleanup;
502     }
503
504     AFS_GLOCK();
505     code = afs_VerifyVCache(avc, areq);
506     AFS_GUNLOCK();
507
508     if (code) {
509         code = afs_CheckCode(code, areq, 11);   /* failed to get it */
510         afs_warn("afs_ReadNoCache Failed to verify VCache!\n");
511         goto cleanup;
512     }
513
514     bparms->areq = areq;
515
516     /* and queue this one */
517     bcnt = 1;
518     AFS_GLOCK();
519     while(bcnt < 20) {
520         breq = afs_BQueue(BOP_FETCH_NOCACHE, avc, B_DONTWAIT, 0, acred, 1, 1,
521                           bparms, (void *)0, (void *)0);
522         if(breq != 0) {
523             code = 0;
524             break;
525         }
526         afs_osi_Wait(10 * bcnt, 0, 0);
527     }
528     AFS_GUNLOCK();
529
530     if(!breq) {
531         code = EBUSY;
532         goto cleanup;
533     }
534
535     return code;
536
537 cleanup:
538     /* If there's a problem before we queue the request, we need to
539      * do everything that would normally happen when the request was
540      * processed, like unlocking the pages and freeing memory.
541      */
542 #ifdef AFS_LINUX24_ENV
543     unlock_and_release_pages(bparms->auio);
544 #else
545 #ifndef UKERNEL
546 #error AFS_CACHE_BYPASS not implemented on this platform
547 #endif
548 #endif
549     osi_Free(areq, sizeof(struct vrequest));
550     osi_Free(bparms->auio->uio_iov,
551              bparms->auio->uio_iovcnt * sizeof(struct iovec));
552     osi_Free(bparms->auio, sizeof(uio_t));
553     osi_Free(bparms, sizeof(struct nocache_read_request));
554     return code;
555 }
556
557
558 /* Cannot have static linkage--called from BPrefetch (afs_daemons) */
559 afs_int32
560 afs_PrefetchNoCache(struct vcache *avc,
561                     afs_ucred_t *acred,
562                     struct nocache_read_request *bparms)
563 {
564     uio_t *auio;
565     struct iovec *iovecp;
566     struct vrequest *areq;
567     afs_int32 code = 0;
568 #ifdef AFS_64BIT_CLIENT
569     afs_int32 length_hi, bytes, locked;
570 #endif
571
572     struct afs_conn *tc;
573     afs_int32 i;
574     struct rx_call *tcall;
575     struct tlocal1 {
576         struct AFSVolSync tsync;
577         struct AFSFetchStatus OutStatus;
578         struct AFSCallBack CallBack;
579     };
580     struct tlocal1 *tcallspec;
581
582     auio = bparms->auio;
583     areq = bparms->areq;
584     iovecp = auio->uio_iov;
585
586     tcallspec = (struct tlocal1 *) osi_Alloc(sizeof(struct tlocal1));
587     do {
588         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK /* ignored */);
589         if (tc) {
590             avc->callback = tc->srvr->server;
591             i = osi_Time();
592             tcall = rx_NewCall(tc->id);
593 #ifdef AFS_64BIT_CLIENT
594             if (!afs_serverHasNo64Bit(tc)) {
595                 code = StartRXAFS_FetchData64(tcall,
596                                               (struct AFSFid *) &avc->f.fid.Fid,
597                                               auio->uio_offset,
598                                               bparms->length);
599                 if (code == 0) {
600                     COND_GUNLOCK(locked);
601                     bytes = rx_Read(tcall, (char *)&length_hi,
602                                     sizeof(afs_int32));
603                     COND_RE_GLOCK(locked);
604
605                     if (bytes != sizeof(afs_int32)) {
606                         length_hi = 0;
607                         code = rx_Error(tcall);
608                         COND_GUNLOCK(locked);
609                         code = rx_EndCall(tcall, code);
610                         COND_RE_GLOCK(locked);
611                         tcall = NULL;
612                     }
613                 }
614             } /* afs_serverHasNo64Bit */
615             if (code == RXGEN_OPCODE || afs_serverHasNo64Bit(tc)) {
616                 if (auio->uio_offset > 0x7FFFFFFF) {
617                     code = EFBIG;
618                 } else {
619                     afs_int32 pos;
620                     pos = auio->uio_offset;
621                     COND_GUNLOCK(locked);
622                     if (!tcall)
623                         tcall = rx_NewCall(tc->id);
624                     code = StartRXAFS_FetchData(tcall,
625                                         (struct AFSFid *) &avc->f.fid.Fid,
626                                         pos, bparms->length);
627                     COND_RE_GLOCK(locked);
628                 }
629                 afs_serverSetNo64Bit(tc);
630             }
631 #else
632             code = StartRXAFS_FetchData(tcall,
633                                         (struct AFSFid *) &avc->f.fid.Fid,
634                                         auio->uio_offset, bparms->length);
635 #endif
636             if (code == 0) {
637                 code = afs_NoCacheFetchProc(tcall, avc, auio,
638                                             1 /* release_pages */,
639                                             bparms->length);
640             } else {
641                 afs_warn("BYPASS: StartRXAFS_FetchData failed: %d\n", code);
642                 unlock_and_release_pages(auio);
643                 goto done;
644             }
645             if (code == 0) {
646                 code = EndRXAFS_FetchData(tcall, &tcallspec->OutStatus,
647                                           &tcallspec->CallBack,
648                                           &tcallspec->tsync);
649             } else {
650                 afs_warn("BYPASS: NoCacheFetchProc failed: %d\n", code);
651             }
652             code = rx_EndCall(tcall, code);
653         } else {
654             afs_warn("BYPASS: No connection.\n");
655             code = -1;
656 #ifdef AFS_LINUX24_ENV
657             unlock_and_release_pages(auio);
658 #else
659 #ifndef UKERNEL
660 #error AFS_CACHE_BYPASS not implemented on this platform
661 #endif
662 #endif
663             goto done;
664         }
665     } while (afs_Analyze(tc, code, &avc->f.fid, areq,
666                                                  AFS_STATS_FS_RPCIDX_FETCHDATA,
667                                                  SHARED_LOCK,0));
668 done:
669     /*
670      * Copy appropriate fields into vcache
671      */
672
673     afs_ProcessFS(avc, &tcallspec->OutStatus, areq);
674
675     osi_Free(areq, sizeof(struct vrequest));
676     osi_Free(tcallspec, sizeof(struct tlocal1));
677     osi_Free(iovecp, auio->uio_iovcnt * sizeof(struct iovec));
678     osi_Free(bparms, sizeof(struct nocache_read_request));
679     osi_Free(auio, sizeof(uio_t));
680     return code;
681 }
682
683 #endif /* AFS_CACHE_BYPASS */