]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_bio.c
xnu-4570.61.1.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_bio.c
index b9d22e786360379987859fb0f6901a3c83c41233..d65d98a1ae98d0461a9dd5cf0a20f411dca59dfa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -97,6 +97,8 @@
 #include <sys/buf_internal.h>
 #include <libkern/OSAtomic.h>
 
+#define NFS_BIO_DBG(...) NFS_DBG(NFS_FAC_BIO, 7, ## __VA_ARGS__)
+
 kern_return_t  thread_terminate(thread_t); /* XXX */
 
 #define        NFSBUFHASH(np, lbn)     \
@@ -346,7 +348,7 @@ nfs_buf_page_inval(vnode_t vp, off_t offset)
        struct nfsbuf *bp;
        int error = 0;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        lck_mtx_lock(nfs_buf_mutex);
@@ -407,8 +409,8 @@ nfs_buf_upl_setup(struct nfsbuf *bp)
                 */
                upl_flags |= UPL_WILL_MODIFY;
        }
-       kret = ubc_create_upl(NFSTOV(bp->nb_np), NBOFF(bp), bp->nb_bufsize,
-                               &upl, NULL, upl_flags);
+       kret = ubc_create_upl_kernel(NFSTOV(bp->nb_np), NBOFF(bp), bp->nb_bufsize,
+                               &upl, NULL, upl_flags, VM_KERN_MEMORY_FILE);
        if (kret == KERN_INVALID_ARGUMENT) {
                /* vm object probably doesn't exist any more */
                bp->nb_pagelist = NULL;
@@ -658,7 +660,7 @@ nfs_buf_get(
        if (bufsize > NFS_MAXBSIZE)
                panic("nfs_buf_get: buffer larger than NFS_MAXBSIZE requested");
 
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                FSDBG_BOT(541, np, blkno, 0, ENXIO);
                return (ENXIO);
        }
@@ -1291,7 +1293,7 @@ nfs_buf_check_write_verifier(nfsnode_t np, struct nfsbuf *bp)
                return;
 
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return;
        if (!ISSET(bp->nb_flags, NB_STALEWVERF) && (bp->nb_verf == nmp->nm_verf))
                return;
@@ -1546,7 +1548,7 @@ nfs_buf_read_rpc(struct nfsbuf *bp, thread_t thd, kauth_cred_t cred)
        struct nfsreq_cbinfo cb;
 
        nmp = NFSTONMP(np);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                bp->nb_error = error = ENXIO;
                SET(bp->nb_flags, NB_ERROR);
                nfs_buf_iodone(bp);
@@ -1669,7 +1671,7 @@ finish:
                nfs_request_ref(req, 0);
 
        nmp = NFSTONMP(np);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                SET(bp->nb_flags, NB_ERROR);
                bp->nb_error = error = ENXIO;
        }
@@ -1842,7 +1844,7 @@ nfs_buf_readahead(nfsnode_t np, int ioflag, daddr64_t *rabnp, daddr64_t lastrabn
        int error = 0;
        uint32_t nra;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        if (nmp->nm_readahead <= 0)
                return (0);
@@ -2327,17 +2329,26 @@ nfs_buf_write(struct nfsbuf *bp)
        thd = async ? NULL : current_thread();
 
        /* We need to make sure the pages are locked before doing I/O.  */
-       if (!ISSET(bp->nb_flags, NB_META) && UBCINFOEXISTS(NFSTOV(np))) {
-               if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
-                       error = nfs_buf_upl_setup(bp);
-                       if (error) {
-                               printf("nfs_buf_write: upl create failed %d\n", error);
-                               SET(bp->nb_flags, NB_ERROR);
-                               bp->nb_error = error = EIO;
-                               nfs_buf_iodone(bp);
-                               goto out;
+       if (!ISSET(bp->nb_flags, NB_META)) {
+               if (UBCINFOEXISTS(NFSTOV(np))) {
+                       if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
+                               error = nfs_buf_upl_setup(bp);
+                               if (error) {
+                                       printf("nfs_buf_write: upl create failed %d\n", error);
+                                       SET(bp->nb_flags, NB_ERROR);
+                                       bp->nb_error = error = EIO;
+                                       nfs_buf_iodone(bp);
+                                       goto out;
+                               }
+                               nfs_buf_upl_check(bp);
                        }
-                       nfs_buf_upl_check(bp);
+               } else {
+                       /* We should never be in nfs_buf_write() with no UBCINFO. */
+                       printf("nfs_buf_write: ubcinfo already gone\n");
+                       SET(bp->nb_flags, NB_ERROR);
+                       bp->nb_error = error = EIO;
+                       nfs_buf_iodone(bp);
+                       goto out;
                }
        }
 
@@ -2346,7 +2357,7 @@ nfs_buf_write(struct nfsbuf *bp)
                nfs_buf_check_write_verifier(np, bp);
        if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
                struct nfsmount *nmp = NFSTONMP(np);
-               if (!nmp) {
+               if (nfs_mount_gone(nmp)) {
                        SET(bp->nb_flags, NB_ERROR);
                        bp->nb_error = error = EIO;
                        nfs_buf_iodone(bp);
@@ -2696,7 +2707,7 @@ nfs_buf_write_rpc(struct nfsbuf *bp, int iomode, thread_t thd, kauth_cred_t cred
        char uio_buf [ UIO_SIZEOF(1) ];
 
        nmp = NFSTONMP(np);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                bp->nb_error = error = ENXIO;
                SET(bp->nb_flags, NB_ERROR);
                nfs_buf_iodone(bp);
@@ -2825,7 +2836,7 @@ finish:
                nfs_request_ref(req, 0);
 
        nmp = NFSTONMP(np);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                SET(bp->nb_flags, NB_ERROR);
                bp->nb_error = error = ENXIO;
        }
@@ -3037,7 +3048,7 @@ nfs_flushcommits(nfsnode_t np, int nowait)
        LIST_INIT(&commitlist);
 
        nmp = NFSTONMP(np);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                error = ENXIO;
                goto done;
        }
@@ -3251,7 +3262,7 @@ nfs_flush(nfsnode_t np, int waitfor, thread_t thd, int ignore_writeerr)
 
        FSDBG_TOP(517, np, waitfor, ignore_writeerr, 0);
 
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                error = ENXIO;
                goto out;
        }
@@ -3620,11 +3631,21 @@ nfs_vinvalbuf2(vnode_t vp, int flags, thread_t thd, kauth_cred_t cred, int intrf
        nfsnode_t np = VTONFS(vp);
        struct nfsmount *nmp = VTONMP(vp);
        int error, slpflag, slptimeo, nflags, retry = 0;
+       int ubcflags = UBC_PUSHALL | UBC_SYNC | UBC_INVALIDATE;
        struct timespec ts = { 2, 0 };
        off_t size;
 
        FSDBG_TOP(554, np, flags, intrflg, 0);
 
+       /*
+        * If the mount is gone no sense to try and write anything.
+        * and hang trying to do IO.
+        */
+       if (nfs_mount_gone(nmp)) {
+               flags &= ~V_SAVE;
+               ubcflags &= ~UBC_PUSHALL;
+       }
+       
        if (nmp && !NMFLAG(nmp, INTR))
                intrflg = 0;
        if (intrflg) {
@@ -3662,14 +3683,16 @@ again:
 
        /* get the pages out of vm also */
        if (UBCINFOEXISTS(vp) && (size = ubc_getsize(vp)))
-               if ((error = ubc_msync(vp, 0, size, NULL, UBC_PUSHALL | UBC_SYNC | UBC_INVALIDATE))) {
+               if ((error = ubc_msync(vp, 0, size, NULL, ubcflags))) {
                        if (error == EINVAL)
                                panic("nfs_vinvalbuf(): ubc_msync failed!, error %d", error);
-                       if (retry++ < 10) /* retry invalidating a few times */
+                       if (retry++ < 10) { /* retry invalidating a few times */
+                               if (retry > 1 || error == ENXIO)
+                                       ubcflags &= ~UBC_PUSHALL;
                                goto again;
+                       }
                        /* give up */
-                       printf("nfs_vinvalbuf(): ubc_msync failed!, error %d", error);
-
+                       printf("nfs_vinvalbuf(): ubc_msync failed!, error %d\n", error);
                }
 done:
        lck_mtx_lock(nfs_buf_mutex);
@@ -3747,8 +3770,11 @@ nfs_asyncio_finish(struct nfsreq *req)
 
        FSDBG_TOP(552, nmp, 0, 0, 0);
 again:
-       if (((nmp = req->r_nmp)) == NULL)
+       nmp = req->r_nmp;
+
+       if (nmp == NULL)
                return;
+
        lck_mtx_lock(nfsiod_mutex);
        niod = nmp->nm_niod;
 
@@ -3773,6 +3799,28 @@ again:
                }
        }
 
+       /*
+        * If we got here while being on the resendq we need to get off. This
+        * happens when the timer fires and errors out requests from nfs_sigintr
+        * or we receive a reply (UDP case) while being on the resend queue so
+        * we're just finishing up and are not going to be resent.
+        */
+       lck_mtx_lock(&req->r_mtx);
+       if (req->r_flags & R_RESENDQ) {
+               lck_mtx_lock(&nmp->nm_lock);
+               if (req->r_rchain.tqe_next != NFSREQNOLIST) {
+                       NFS_BIO_DBG("Proccessing async request on resendq. Removing");
+                       TAILQ_REMOVE(&nmp->nm_resendq, req, r_rchain);
+                       req->r_rchain.tqe_next = NFSREQNOLIST;
+                       assert(req->r_refs > 1);
+                       /* Remove resendq reference */
+                       req->r_refs--;
+               }
+               lck_mtx_unlock(&nmp->nm_lock);
+               req->r_flags &= ~R_RESENDQ;
+       }
+       lck_mtx_unlock(&req->r_mtx);
+
        if (req->r_achain.tqe_next == NFSREQNOLIST)
                TAILQ_INSERT_TAIL(&nmp->nm_iodq, req, r_achain);
 
@@ -3783,8 +3831,9 @@ again:
                        lck_mtx_unlock(nfsiod_mutex);
                        wakeup(niod);
                } else if (nfsiod_thread_count > 0) {
-                       /* just queue it up on nfsiod mounts queue */
-                       TAILQ_INSERT_TAIL(&nfsiodmounts, nmp, nm_iodlink);
+                       /* just queue it up on nfsiod mounts queue if needed */
+                       if (nmp->nm_iodlink.tqe_next == NFSNOLIST)
+                               TAILQ_INSERT_TAIL(&nfsiodmounts, nmp, nm_iodlink);
                        lck_mtx_unlock(nfsiod_mutex);
                } else {
                        printf("nfs_asyncio(): no nfsiods? %d %d (%d)\n", nfsiod_thread_count, NFSIOD_MAX, started);
@@ -3808,13 +3857,19 @@ nfs_asyncio_resend(struct nfsreq *req)
 {
        struct nfsmount *nmp = req->r_nmp;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return;
+
        nfs_gss_clnt_rpcdone(req);
        lck_mtx_lock(&nmp->nm_lock);
        if (!(req->r_flags & R_RESENDQ)) {
                TAILQ_INSERT_TAIL(&nmp->nm_resendq, req, r_rchain);
                req->r_flags |= R_RESENDQ;
+               /*
+                * We take a reference on this request so that it can't be
+                * destroyed while a resend is queued or in progress.
+                */
+               nfs_request_ref(req, 1);
        }
        nfs_mount_sock_thread_wake(nmp);
        lck_mtx_unlock(&nmp->nm_lock);
@@ -3833,7 +3888,7 @@ nfs_buf_readdir(struct nfsbuf *bp, vfs_context_t ctx)
        struct nfsmount *nmp = NFSTONMP(np);
        int error = 0;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        if (nmp->nm_vers < NFS_VER4)