Linux 3.6: kmap_atomic API change
[openafs.git] / src / afs / afs_bypasscache.c
index 8d7e601..b8211b8 100644 (file)
@@ -60,9 +60,7 @@
 
 #include <afsconfig.h>
 #include "afs/param.h"
-
-#if defined(AFS_CACHE_BYPASS)
-
+#if defined(AFS_CACHE_BYPASS) || defined(UKERNEL)
 #include "afs/afs_bypasscache.h"
 
 /*
 #include "afs/nfsclient.h"
 #include "rx/rx_globals.h"
 
-#if defined(AFS_LINUX26_ENV)
-#define LockPage(pp) lock_page(pp)
-#define UnlockPage(pp) unlock_page(pp)
-#endif
-
 #ifndef afs_min
 #define afs_min(A,B) ((A)<(B)) ? (A) : (B)
 #endif
@@ -158,12 +151,12 @@ afs_TransitionToBypass(struct vcache *avc,
      * Someone may have beat us to doing the transition - we had no lock
      * when we checked the flag earlier.  No cause to panic, just return.
      */
-    if (avc->f.states & FCSBypass)
+    if (avc->cachingStates & FCSBypass)
        goto done;
 
     /* If we never cached this, just change state */
     if (setDesire && (!(avc->cachingStates & FCSBypass))) {
-       avc->f.states |= FCSBypass;
+       avc->cachingStates |= FCSBypass;
        goto done;
     }
 
@@ -238,7 +231,7 @@ afs_TransitionToCaching(struct vcache *avc,
      * Someone may have beat us to doing the transition - we had no lock
      * when we checked the flag earlier.  No cause to panic, just return.
      */
-    if (!(avc->f.states & FCSBypass))
+    if (!(avc->cachingStates & FCSBypass))
        goto done;
 
     /* Ok, we actually do need to flush */
@@ -274,67 +267,113 @@ done:
  * afs_PrefetchNoCache, all of the pages they've been passed need
  * to be unlocked.
  */
-#if defined(AFS_LINUX24_ENV)
+#ifdef UKERNEL
+typedef void * bypass_page_t;
+
+#define unlock_and_release_pages(auio)
+#define release_full_page(pp, pageoff)
+
+#else
+typedef struct page * bypass_page_t;
+
 #define unlock_and_release_pages(auio) \
     do { \
        struct iovec *ciov;     \
-       struct page *pp; \
+       bypass_page_t pp; \
        afs_int32 iovmax; \
        afs_int32 iovno = 0; \
        ciov = auio->uio_iov; \
        iovmax = auio->uio_iovcnt - 1;  \
-       pp = (struct page*) ciov->iov_base;     \
+       pp = (bypass_page_t) ciov->iov_base;    \
        while(1) { \
            if (pp) { \
                if (PageLocked(pp)) \
-                   UnlockPage(pp);     \
+                   unlock_page(pp);    \
                put_page(pp); /* decrement refcount */ \
            } \
            iovno++; \
            if(iovno > iovmax) \
                break; \
            ciov = (auio->uio_iov + iovno);     \
-           pp = (struct page*) ciov->iov_base; \
+           pp = (bypass_page_t) ciov->iov_base;        \
        } \
     } while(0)
+
+#define release_full_page(pp, pageoff)                 \
+    do { \
+       /* this is appropriate when no caller intends to unlock \
+        * and release the page */ \
+       SetPageUptodate(pp); \
+       if(PageLocked(pp)) \
+           unlock_page(pp); \
+       else \
+           afs_warn("afs_NoCacheFetchProc: page not locked!\n"); \
+       put_page(pp); /* decrement refcount */ \
+    } while(0)
+#endif
+
+static void
+afs_bypass_copy_page(bypass_page_t pp, int pageoff, struct iovec *rxiov,
+       int iovno, int iovoff, struct uio *auio, int curiov, int partial)
+{
+    char *address;
+    int dolen;
+
+    if (partial)
+       dolen = rxiov[iovno].iov_len - iovoff;
+    else
+       dolen = auio->uio_iov[curiov].iov_len - pageoff;
+
+#if !defined(UKERNEL)
+# if defined(KMAP_ATOMIC_TAKES_NO_KM_TYPE)
+    address = kmap_atomic(pp);
+# else
+    address = kmap_atomic(pp, KM_USER0);
+# endif
 #else
-#ifdef UKERNEL
-#define unlock_and_release_pages(auio) \
-        do { } while(0)
-#else
-#error AFS_CACHE_BYPASS not implemented on this platform
+    address = pp;
 #endif
+    memcpy(address + pageoff, (char *)(rxiov[iovno].iov_base) + iovoff, dolen);
+#if !defined(UKERNEL)
+# if defined(KMAP_ATOMIC_TAKES_NO_KM_TYPE)
+    kunmap_atomic(address);
+# else
+    kunmap_atomic(address, KM_USER0);
+# endif
 #endif
+}
 
 /* no-cache prefetch routine */
 static afs_int32
 afs_NoCacheFetchProc(struct rx_call *acall,
                      struct vcache *avc,
-                                        uio_t *auio,
+                    struct uio *auio,
                      afs_int32 release_pages,
                     afs_int32 size)
 {
     afs_int32 length;
     afs_int32 code;
-    int tlen;
-    int moredata, iovno, iovoff, iovmax, clen, result, locked;
+    int moredata, iovno, iovoff, iovmax, result, locked;
     struct iovec *ciov;
-    struct page *pp;
-    char *address;
-    char *page_buffer = osi_Alloc(PAGE_SIZE);
+    struct iovec *rxiov;
+    int nio = 0;
+    bypass_page_t pp;
 
+    int curpage, bytes;
+    int pageoff;
+
+    rxiov = osi_AllocSmallSpace(sizeof(struct iovec) * RX_MAXIOVECS);
     ciov = auio->uio_iov;
-    pp = (struct page*) ciov->iov_base;
+    pp = (bypass_page_t) ciov->iov_base;
     iovmax = auio->uio_iovcnt - 1;
     iovno = iovoff = result = 0;
-    do {
 
+    do {
        COND_GUNLOCK(locked);
        code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
        COND_RE_GLOCK(locked);
-
        if (code != sizeof(afs_int32)) {
-           result = 0;
+           result = EIO;
            afs_warn("Preread error. code: %d instead of %d\n",
                code, (int)sizeof(afs_int32));
            unlock_and_release_pages(auio);
@@ -373,82 +412,61 @@ afs_NoCacheFetchProc(struct rx_call *acall,
            moredata = 0;
        }
 
-       while (length > 0) {
-
-           clen = ciov->iov_len - iovoff;
-           tlen = afs_min(length, clen);
-#ifdef AFS_LINUX24_ENV
-           address = page_buffer;
-#else
-#ifndef UKERNEL
-#error AFS_CACHE_BYPASS not implemented on this platform
-#endif
-#endif /* LINUX24 */
-           COND_GUNLOCK(locked);
-           code = rx_Read(acall, address, tlen);
-           COND_RE_GLOCK(locked);
-
-           if (code < 0) {
-               afs_warn("afs_NoCacheFetchProc: rx_Read error. Return code was %d\n", code);
-               result = 0;
-               unlock_and_release_pages(auio);
-               goto done;
-           } else if (code == 0) {
-               result = 0;
-               afs_warn("afs_NoCacheFetchProc: rx_Read returned zero. Aborting.\n");
-               unlock_and_release_pages(auio);
-               goto done;
-           }
-           length -= code;
-           tlen -= code;
-
-           if(tlen > 0) {
-               iovoff += code;
-               address += code;
-           } else {
-#ifdef AFS_LINUX24_ENV
-               if(pp) {
-                   address = kmap_atomic(pp, KM_USER0);
-                   memcpy(address, page_buffer, PAGE_SIZE);
-                   kunmap_atomic(address, KM_USER0);
+       for (curpage = 0; curpage <= iovmax; curpage++) {
+           pageoff = 0;
+           /* properly, this should track uio_resid, not a fixed page size! */
+           while (pageoff < auio->uio_iov[curpage].iov_len) {
+               /* If no more iovs, issue new read. */
+               if (iovno >= nio) {
+                   COND_GUNLOCK(locked);
+                   bytes = rx_Readv(acall, rxiov, &nio, RX_MAXIOVECS, length);
+                   COND_RE_GLOCK(locked);
+                   if (bytes < 0) {
+                       afs_warn("afs_NoCacheFetchProc: rx_Read error. Return code was %d\n", bytes);
+                       result = bytes;
+                       unlock_and_release_pages(auio);
+                       goto done;
+                   } else if (bytes == 0) {
+                       /* we failed to read the full length */
+                       result = EIO;
+                       afs_warn("afs_NoCacheFetchProc: rx_Read returned zero. Aborting.\n");
+                       unlock_and_release_pages(auio);
+                       goto done;
+                   }
+                   size -= bytes;
+                   iovno = 0;
                }
-#else
-#ifndef UKERNEL
-#error AFS_CACHE_BYPASS not implemented on this platform
-#endif
-#endif /* LINUX 24 */
-               /* we filled a page, conditionally release it */
-               if (release_pages && ciov->iov_base) {
-                   /* this is appropriate when no caller intends to unlock
-                    * and release the page */
-#ifdef AFS_LINUX24_ENV
-                    SetPageUptodate(pp);
-                    if(PageLocked(pp))
-                        UnlockPage(pp);
-                    else
-                        afs_warn("afs_NoCacheFetchProc: page not locked at iovno %d!\n", iovno);
-                    put_page(pp); /* decrement refcount */
-#else
-#ifndef UKERNEL
-#error AFS_CACHE_BYPASS not implemented on this platform
-#endif
-#endif /* LINUX24 */
+               pp = (bypass_page_t)auio->uio_iov[curpage].iov_base;
+               if (pageoff + (rxiov[iovno].iov_len - iovoff) <= auio->uio_iov[curpage].iov_len) {
+                   /* Copy entire (or rest of) current iovec into current page */
+                   if (pp)
+                       afs_bypass_copy_page(pp, pageoff, rxiov, iovno, iovoff, auio, curpage, 0);
+                   length -= (rxiov[iovno].iov_len - iovoff);
+                   pageoff += rxiov[iovno].iov_len - iovoff;
+                   iovno++;
+                   iovoff = 0;
+               } else {
+                   /* Copy only what's needed to fill current page */
+                   if (pp)
+                       afs_bypass_copy_page(pp, pageoff, rxiov, iovno, iovoff, auio, curpage, 1);
+                   length -= (auio->uio_iov[curpage].iov_len - pageoff);
+                   iovoff += auio->uio_iov[curpage].iov_len - pageoff;
+                   pageoff = auio->uio_iov[curpage].iov_len;
                }
-               /* and carry uio_iov */
-               iovno++;
-               if (iovno > iovmax)
-                   goto done;
 
-               ciov = (auio->uio_iov + iovno);
-               pp = (struct page*) ciov->iov_base;
-               iovoff = 0;
+               /* we filled a page, or this is the last page.  conditionally release it */
+               if (pp && ((pageoff == auio->uio_iov[curpage].iov_len &&
+                           release_pages) || (length == 0 && iovno >= nio)))
+                   release_full_page(pp, pageoff);
+
+               if (length == 0 && iovno >= nio)
+                   goto done;
            }
        }
     } while (moredata);
 
 done:
-    if(page_buffer)
-       osi_Free(page_buffer, PAGE_SIZE);
+    osi_FreeSmallSpace(rxiov);
     return result;
 }
 
@@ -467,7 +485,7 @@ afs_ReadNoCache(struct vcache *avc,
     /* the reciever will free this */
     areq = osi_Alloc(sizeof(struct vrequest));
 
-    if (avc && avc->vc_error) {
+    if (avc->vc_error) {
        code = EIO;
        afs_warn("afs_ReadNoCache VCache Error!\n");
        goto cleanup;
@@ -515,17 +533,11 @@ cleanup:
      * do everything that would normally happen when the request was
      * processed, like unlocking the pages and freeing memory.
      */
-#ifdef AFS_LINUX24_ENV
     unlock_and_release_pages(bparms->auio);
-#else
-#ifndef UKERNEL
-#error AFS_CACHE_BYPASS not implemented on this platform
-#endif
-#endif
     osi_Free(areq, sizeof(struct vrequest));
     osi_Free(bparms->auio->uio_iov,
             bparms->auio->uio_iovcnt * sizeof(struct iovec));
-    osi_Free(bparms->auio, sizeof(uio_t));
+    osi_Free(bparms->auio, sizeof(struct uio));
     osi_Free(bparms, sizeof(struct nocache_read_request));
     return code;
 }
@@ -537,16 +549,18 @@ afs_PrefetchNoCache(struct vcache *avc,
                    afs_ucred_t *acred,
                    struct nocache_read_request *bparms)
 {
-    uio_t *auio;
+    struct uio *auio;
+#ifndef UKERNEL
     struct iovec *iovecp;
+#endif
     struct vrequest *areq;
     afs_int32 code = 0;
+    struct rx_connection *rxconn;
 #ifdef AFS_64BIT_CLIENT
     afs_int32 length_hi, bytes, locked;
 #endif
 
     struct afs_conn *tc;
-    afs_int32 i;
     struct rx_call *tcall;
     struct tlocal1 {
        struct AFSVolSync tsync;
@@ -557,15 +571,16 @@ afs_PrefetchNoCache(struct vcache *avc,
 
     auio = bparms->auio;
     areq = bparms->areq;
+#ifndef UKERNEL
     iovecp = auio->uio_iov;
+#endif
 
-    tcallspec = (struct tlocal1 *) osi_Alloc(sizeof(struct tlocal1));
+    tcallspec = osi_Alloc(sizeof(struct tlocal1));
     do {
-       tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK /* ignored */);
+       tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK /* ignored */, &rxconn);
        if (tc) {
-           avc->callback = tc->srvr->server;
-           i = osi_Time();
-           tcall = rx_NewCall(tc->id);
+           avc->callback = tc->parent->srvr->server;
+           tcall = rx_NewCall(rxconn);
 #ifdef AFS_64BIT_CLIENT
            if (!afs_serverHasNo64Bit(tc)) {
                code = StartRXAFS_FetchData64(tcall,
@@ -596,7 +611,7 @@ afs_PrefetchNoCache(struct vcache *avc,
                    pos = auio->uio_offset;
                    COND_GUNLOCK(locked);
                    if (!tcall)
-                       tcall = rx_NewCall(tc->id);
+                       tcall = rx_NewCall(rxconn);
                    code = StartRXAFS_FetchData(tcall,
                                        (struct AFSFid *) &avc->f.fid.Fid,
                                        pos, bparms->length);
@@ -629,16 +644,10 @@ afs_PrefetchNoCache(struct vcache *avc,
        } else {
            afs_warn("BYPASS: No connection.\n");
            code = -1;
-#ifdef AFS_LINUX24_ENV
            unlock_and_release_pages(auio);
-#else
-#ifndef UKERNEL
-#error AFS_CACHE_BYPASS not implemented on this platform
-#endif
-#endif
            goto done;
        }
-    } while (afs_Analyze(tc, code, &avc->f.fid, areq,
+    } while (afs_Analyze(tc, rxconn, code, &avc->f.fid, areq,
                                                 AFS_STATS_FS_RPCIDX_FETCHDATA,
                                                 SHARED_LOCK,0));
 done:
@@ -646,14 +655,17 @@ done:
      * Copy appropriate fields into vcache
      */
 
-    afs_ProcessFS(avc, &tcallspec->OutStatus, areq);
+    if (!code)
+       afs_ProcessFS(avc, &tcallspec->OutStatus, areq);
 
     osi_Free(areq, sizeof(struct vrequest));
     osi_Free(tcallspec, sizeof(struct tlocal1));
-    osi_Free(iovecp, auio->uio_iovcnt * sizeof(struct iovec));
     osi_Free(bparms, sizeof(struct nocache_read_request));
-    osi_Free(auio, sizeof(uio_t));
+#ifndef UKERNEL
+    /* in UKERNEL, the "pages" are passed in */
+    osi_Free(iovecp, auio->uio_iovcnt * sizeof(struct iovec));
+    osi_Free(auio, sizeof(struct uio));
+#endif
     return code;
 }
-
-#endif /* AFS_CACHE_BYPASS */
+#endif