152e1cf57SMatthew Dillon /* 252e1cf57SMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved. 352e1cf57SMatthew Dillon * 452e1cf57SMatthew Dillon * This code is derived from software contributed to The DragonFly Project 552e1cf57SMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 652e1cf57SMatthew Dillon * 752e1cf57SMatthew Dillon * Redistribution and use in source and binary forms, with or without 852e1cf57SMatthew Dillon * modification, are permitted provided that the following conditions 952e1cf57SMatthew Dillon * are met: 1052e1cf57SMatthew Dillon * 1152e1cf57SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 1252e1cf57SMatthew Dillon * notice, this list of conditions and the following disclaimer. 1352e1cf57SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 1452e1cf57SMatthew Dillon * notice, this list of conditions and the following disclaimer in 1552e1cf57SMatthew Dillon * the documentation and/or other materials provided with the 1652e1cf57SMatthew Dillon * distribution. 1752e1cf57SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 1852e1cf57SMatthew Dillon * contributors may be used to endorse or promote products derived 1952e1cf57SMatthew Dillon * from this software without specific, prior written permission. 2052e1cf57SMatthew Dillon * 2152e1cf57SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2252e1cf57SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2352e1cf57SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2452e1cf57SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2552e1cf57SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2652e1cf57SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2752e1cf57SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2852e1cf57SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2952e1cf57SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3052e1cf57SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3152e1cf57SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3252e1cf57SMatthew Dillon * SUCH DAMAGE. 3352e1cf57SMatthew Dillon */ 3452e1cf57SMatthew Dillon /* 3552e1cf57SMatthew Dillon * NFSIOD operations - now built into the kernel. 3652e1cf57SMatthew Dillon */ 3752e1cf57SMatthew Dillon #include <sys/param.h> 3852e1cf57SMatthew Dillon #include <sys/systm.h> 3952e1cf57SMatthew Dillon #include <sys/proc.h> 4052e1cf57SMatthew Dillon #include <sys/malloc.h> 4152e1cf57SMatthew Dillon #include <sys/mount.h> 4252e1cf57SMatthew Dillon #include <sys/kernel.h> 4352e1cf57SMatthew Dillon #include <sys/mbuf.h> 4452e1cf57SMatthew Dillon #include <sys/vnode.h> 4552e1cf57SMatthew Dillon #include <sys/fcntl.h> 4652e1cf57SMatthew Dillon #include <sys/protosw.h> 4752e1cf57SMatthew Dillon #include <sys/resourcevar.h> 4852e1cf57SMatthew Dillon #include <sys/socket.h> 4952e1cf57SMatthew Dillon #include <sys/socketvar.h> 5052e1cf57SMatthew Dillon #include <sys/socketops.h> 5152e1cf57SMatthew Dillon #include <sys/syslog.h> 5252e1cf57SMatthew Dillon #include <sys/thread.h> 5352e1cf57SMatthew Dillon #include <sys/tprintf.h> 5452e1cf57SMatthew Dillon #include <sys/sysctl.h> 5552e1cf57SMatthew Dillon #include <sys/signalvar.h> 5652e1cf57SMatthew Dillon #include <sys/mutex.h> 5752e1cf57SMatthew Dillon 5852e1cf57SMatthew Dillon #include <sys/signal2.h> 59*c9e9fb21SMatthew Dillon #include <sys/thread2.h> 6052e1cf57SMatthew Dillon #include <sys/mutex2.h> 61*c9e9fb21SMatthew Dillon #include <sys/mplock2.h> 6252e1cf57SMatthew Dillon 6352e1cf57SMatthew Dillon #include <netinet/in.h> 6452e1cf57SMatthew Dillon #include <netinet/tcp.h> 6552e1cf57SMatthew Dillon 6652e1cf57SMatthew Dillon #include "rpcv2.h" 6752e1cf57SMatthew Dillon #include "nfsproto.h" 6852e1cf57SMatthew Dillon #include "nfs.h" 6952e1cf57SMatthew Dillon #include "xdr_subs.h" 7052e1cf57SMatthew Dillon #include "nfsm_subs.h" 7152e1cf57SMatthew Dillon #include "nfsmount.h" 7252e1cf57SMatthew Dillon #include "nfsnode.h" 7352e1cf57SMatthew Dillon #include "nfsrtt.h" 7452e1cf57SMatthew Dillon 75*c9e9fb21SMatthew Dillon /* 76*c9e9fb21SMatthew Dillon * nfs service connection reader thread 77*c9e9fb21SMatthew Dillon */ 7852e1cf57SMatthew Dillon void 7952e1cf57SMatthew Dillon nfssvc_iod_reader(void *arg) 8052e1cf57SMatthew Dillon { 8152e1cf57SMatthew Dillon struct nfsmount *nmp = arg; 82edb90c22SMatthew Dillon struct nfsm_info *info; 83edb90c22SMatthew Dillon struct nfsreq *req; 84edb90c22SMatthew Dillon int error; 8552e1cf57SMatthew Dillon 86*c9e9fb21SMatthew Dillon get_mplock(); 87*c9e9fb21SMatthew Dillon 8852e1cf57SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_INIT) 8952e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_PENDING; 90f8565b0fSMatthew Dillon crit_enter(); 9152e1cf57SMatthew Dillon for (;;) { 9252e1cf57SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_WAITING) { 93edb90c22SMatthew Dillon if (TAILQ_FIRST(&nmp->nm_reqq) == NULL && 94edb90c22SMatthew Dillon TAILQ_FIRST(&nmp->nm_reqrxq) == NULL) { 9552e1cf57SMatthew Dillon tsleep(&nmp->nm_rxstate, 0, "nfsidl", 0); 96edb90c22SMatthew Dillon } else { 97edb90c22SMatthew Dillon /* 98edb90c22SMatthew Dillon * This can happen during shutdown, we don't 99edb90c22SMatthew Dillon * want to hardloop. 100edb90c22SMatthew Dillon */ 101edb90c22SMatthew Dillon error = nfs_reply(nmp, NULL); 102edb90c22SMatthew Dillon if (error && error != EWOULDBLOCK) { 103edb90c22SMatthew Dillon tsleep(&nmp->nm_rxstate, 0, 104edb90c22SMatthew Dillon "nfsxxx", hz / 10); 105edb90c22SMatthew Dillon } 106edb90c22SMatthew Dillon } 10752e1cf57SMatthew Dillon continue; 10852e1cf57SMatthew Dillon } 10952e1cf57SMatthew Dillon if (nmp->nm_rxstate != NFSSVC_PENDING) 11052e1cf57SMatthew Dillon break; 11152e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_WAITING; 11252e1cf57SMatthew Dillon 113edb90c22SMatthew Dillon /* 114edb90c22SMatthew Dillon * Process requests which have received replies. Only 115edb90c22SMatthew Dillon * process the post-reply states. If we get EINPROGRESS 116edb90c22SMatthew Dillon * it means the request went back to an auth or retransmit 117edb90c22SMatthew Dillon * state and we let the iod_writer thread deal with it. 118edb90c22SMatthew Dillon * 119f8565b0fSMatthew Dillon * Any lock on the request is strictly temporary due to 120f8565b0fSMatthew Dillon * MP races (XXX). 121f8565b0fSMatthew Dillon * 122edb90c22SMatthew Dillon * If the request completes we run the info->done call 123edb90c22SMatthew Dillon * to finish up the I/O. 124edb90c22SMatthew Dillon */ 125edb90c22SMatthew Dillon while ((req = TAILQ_FIRST(&nmp->nm_reqrxq)) != NULL) { 126f8565b0fSMatthew Dillon if (req->r_flags & R_LOCKED) { 127f8565b0fSMatthew Dillon while (req->r_flags & R_LOCKED) { 128f8565b0fSMatthew Dillon req->r_flags |= R_WANTED; 129f8565b0fSMatthew Dillon tsleep(req, 0, "nfstrac", 0); 130f8565b0fSMatthew Dillon } 131f8565b0fSMatthew Dillon continue; 132f8565b0fSMatthew Dillon } 133edb90c22SMatthew Dillon TAILQ_REMOVE(&nmp->nm_reqrxq, req, r_chain); 134f8565b0fSMatthew Dillon crit_exit(); 135edb90c22SMatthew Dillon info = req->r_info; 136edb90c22SMatthew Dillon KKASSERT(info); 137edb90c22SMatthew Dillon info->error = nfs_request(info, 138edb90c22SMatthew Dillon NFSM_STATE_PROCESSREPLY, 139edb90c22SMatthew Dillon NFSM_STATE_DONE); 140edb90c22SMatthew Dillon if (info->error == EINPROGRESS) { 141edb90c22SMatthew Dillon kprintf("rxq: move info %p back to txq\n", info); 142edb90c22SMatthew Dillon TAILQ_INSERT_TAIL(&nmp->nm_reqtxq, req, r_chain); 143edb90c22SMatthew Dillon nfssvc_iod_writer_wakeup(nmp); 144edb90c22SMatthew Dillon } else { 145f8565b0fSMatthew Dillon atomic_subtract_int(&nmp->nm_bioqlen, 1); 146edb90c22SMatthew Dillon info->done(info); 147edb90c22SMatthew Dillon } 148f8565b0fSMatthew Dillon crit_enter(); 149edb90c22SMatthew Dillon } 15052e1cf57SMatthew Dillon } 151f8565b0fSMatthew Dillon crit_exit(); 15252e1cf57SMatthew Dillon nmp->nm_rxthread = NULL; 15352e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_DONE; 15452e1cf57SMatthew Dillon wakeup(&nmp->nm_rxthread); 15552e1cf57SMatthew Dillon } 15652e1cf57SMatthew Dillon 15752e1cf57SMatthew Dillon /* 158*c9e9fb21SMatthew Dillon * nfs service connection writer thread 159*c9e9fb21SMatthew Dillon * 16052e1cf57SMatthew Dillon * The writer sits on the send side of the client's socket and 16152e1cf57SMatthew Dillon * does both the initial processing of BIOs and also transmission 16252e1cf57SMatthew Dillon * and retransmission of nfsreq's. 163edb90c22SMatthew Dillon * 164edb90c22SMatthew Dillon * The writer processes both new BIOs from nm_bioq and retransmit 165edb90c22SMatthew Dillon * or state machine jumpbacks from nm_reqtxq 16652e1cf57SMatthew Dillon */ 16752e1cf57SMatthew Dillon void 16852e1cf57SMatthew Dillon nfssvc_iod_writer(void *arg) 16952e1cf57SMatthew Dillon { 17052e1cf57SMatthew Dillon struct nfsmount *nmp = arg; 17152e1cf57SMatthew Dillon struct bio *bio; 172edb90c22SMatthew Dillon struct nfsreq *req; 17352e1cf57SMatthew Dillon struct vnode *vp; 174edb90c22SMatthew Dillon nfsm_info_t info; 17552e1cf57SMatthew Dillon 176*c9e9fb21SMatthew Dillon get_mplock(); 177*c9e9fb21SMatthew Dillon 17852e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_INIT) 17952e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_PENDING; 180*c9e9fb21SMatthew Dillon 181f8565b0fSMatthew Dillon crit_enter(); 18252e1cf57SMatthew Dillon for (;;) { 18352e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_WAITING) { 18452e1cf57SMatthew Dillon tsleep(&nmp->nm_txstate, 0, "nfsidl", 0); 18552e1cf57SMatthew Dillon continue; 18652e1cf57SMatthew Dillon } 18752e1cf57SMatthew Dillon if (nmp->nm_txstate != NFSSVC_PENDING) 18852e1cf57SMatthew Dillon break; 18952e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_WAITING; 19052e1cf57SMatthew Dillon 191cc7d050eSMatthew Dillon /* 192cc7d050eSMatthew Dillon * Eep, we could blow out the mbuf allocator if we just 193cc7d050eSMatthew Dillon * did everything the kernel wanted us to do. 194cc7d050eSMatthew Dillon */ 195f8565b0fSMatthew Dillon while ((bio = TAILQ_FIRST(&nmp->nm_bioq)) != NULL) { 196b9a7a2bdSMatthew Dillon if (nmp->nm_reqqlen > nfs_maxasyncbio) 197cc7d050eSMatthew Dillon break; 19852e1cf57SMatthew Dillon TAILQ_REMOVE(&nmp->nm_bioq, bio, bio_act); 19952e1cf57SMatthew Dillon vp = bio->bio_driver_info; 200f8565b0fSMatthew Dillon crit_exit(); 201edb90c22SMatthew Dillon nfs_startio(vp, bio, NULL); 202f8565b0fSMatthew Dillon crit_enter(); 203edb90c22SMatthew Dillon } 204edb90c22SMatthew Dillon 205edb90c22SMatthew Dillon /* 206edb90c22SMatthew Dillon * Process reauths & retransmits. If we get an EINPROGRESS 207edb90c22SMatthew Dillon * it means the state transitioned to WAITREPLY or later. 208edb90c22SMatthew Dillon * Otherwise the request completed (probably with an error 209edb90c22SMatthew Dillon * since we didn't get to a replied state). 210edb90c22SMatthew Dillon */ 211edb90c22SMatthew Dillon while ((req = TAILQ_FIRST(&nmp->nm_reqtxq)) != NULL) { 212edb90c22SMatthew Dillon TAILQ_REMOVE(&nmp->nm_reqtxq, req, r_chain); 213edb90c22SMatthew Dillon info = req->r_info; 214edb90c22SMatthew Dillon KKASSERT(info); 215f8565b0fSMatthew Dillon crit_exit(); 216edb90c22SMatthew Dillon info->error = nfs_request(info, 217edb90c22SMatthew Dillon NFSM_STATE_AUTH, 218edb90c22SMatthew Dillon NFSM_STATE_WAITREPLY); 219f8565b0fSMatthew Dillon crit_enter(); 220edb90c22SMatthew Dillon if (info->error == EINPROGRESS) { 221f8565b0fSMatthew Dillon ; 222edb90c22SMatthew Dillon } else { 223f8565b0fSMatthew Dillon atomic_subtract_int(&nmp->nm_bioqlen, 1); 224edb90c22SMatthew Dillon info->done(info); 225edb90c22SMatthew Dillon } 22652e1cf57SMatthew Dillon } 22752e1cf57SMatthew Dillon } 228f8565b0fSMatthew Dillon crit_exit(); 22952e1cf57SMatthew Dillon nmp->nm_txthread = NULL; 23052e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_DONE; 23152e1cf57SMatthew Dillon wakeup(&nmp->nm_txthread); 23252e1cf57SMatthew Dillon } 23352e1cf57SMatthew Dillon 23452e1cf57SMatthew Dillon void 23513ddc895SMatthew Dillon nfssvc_iod_stop1(struct nfsmount *nmp) 23652e1cf57SMatthew Dillon { 23713ddc895SMatthew Dillon crit_enter(); 23852e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_STOPPING; 23913ddc895SMatthew Dillon nmp->nm_rxstate = NFSSVC_STOPPING; 24013ddc895SMatthew Dillon crit_exit(); 24113ddc895SMatthew Dillon } 24213ddc895SMatthew Dillon 24313ddc895SMatthew Dillon void 24413ddc895SMatthew Dillon nfssvc_iod_stop2(struct nfsmount *nmp) 24513ddc895SMatthew Dillon { 24652e1cf57SMatthew Dillon wakeup(&nmp->nm_txstate); 24752e1cf57SMatthew Dillon while (nmp->nm_txthread) 248a63246d1SMatthew Dillon tsleep(&nmp->nm_txthread, 0, "nfssttx", hz*2); 24952e1cf57SMatthew Dillon wakeup(&nmp->nm_rxstate); 25052e1cf57SMatthew Dillon while (nmp->nm_rxthread) 251a63246d1SMatthew Dillon tsleep(&nmp->nm_rxthread, 0, "nfsstrx", hz*2); 25252e1cf57SMatthew Dillon } 25352e1cf57SMatthew Dillon 25452e1cf57SMatthew Dillon void 25552e1cf57SMatthew Dillon nfssvc_iod_writer_wakeup(struct nfsmount *nmp) 25652e1cf57SMatthew Dillon { 25752e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_WAITING) { 25852e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_PENDING; 25952e1cf57SMatthew Dillon wakeup(&nmp->nm_txstate); 26052e1cf57SMatthew Dillon } 26152e1cf57SMatthew Dillon } 262edb90c22SMatthew Dillon 263edb90c22SMatthew Dillon void 264edb90c22SMatthew Dillon nfssvc_iod_reader_wakeup(struct nfsmount *nmp) 265edb90c22SMatthew Dillon { 266edb90c22SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_WAITING) { 267edb90c22SMatthew Dillon nmp->nm_rxstate = NFSSVC_PENDING; 268edb90c22SMatthew Dillon wakeup(&nmp->nm_rxstate); 269edb90c22SMatthew Dillon } 270edb90c22SMatthew Dillon } 271