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> 5952e1cf57SMatthew Dillon #include <sys/mutex2.h> 6052e1cf57SMatthew Dillon 6152e1cf57SMatthew Dillon #include <netinet/in.h> 6252e1cf57SMatthew Dillon #include <netinet/tcp.h> 6352e1cf57SMatthew Dillon #include <sys/thread2.h> 6452e1cf57SMatthew Dillon 6552e1cf57SMatthew Dillon #include "rpcv2.h" 6652e1cf57SMatthew Dillon #include "nfsproto.h" 6752e1cf57SMatthew Dillon #include "nfs.h" 6852e1cf57SMatthew Dillon #include "xdr_subs.h" 6952e1cf57SMatthew Dillon #include "nfsm_subs.h" 7052e1cf57SMatthew Dillon #include "nfsmount.h" 7152e1cf57SMatthew Dillon #include "nfsnode.h" 7252e1cf57SMatthew Dillon #include "nfsrtt.h" 7352e1cf57SMatthew Dillon 7452e1cf57SMatthew Dillon void 7552e1cf57SMatthew Dillon nfssvc_iod_reader(void *arg) 7652e1cf57SMatthew Dillon { 7752e1cf57SMatthew Dillon struct nfsmount *nmp = arg; 78edb90c22SMatthew Dillon struct nfsm_info *info; 79edb90c22SMatthew Dillon struct nfsreq *req; 80edb90c22SMatthew Dillon int error; 8152e1cf57SMatthew Dillon 8252e1cf57SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_INIT) 8352e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_PENDING; 84f8565b0fSMatthew Dillon crit_enter(); 8552e1cf57SMatthew Dillon for (;;) { 8652e1cf57SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_WAITING) { 87edb90c22SMatthew Dillon if (TAILQ_FIRST(&nmp->nm_reqq) == NULL && 88edb90c22SMatthew Dillon TAILQ_FIRST(&nmp->nm_reqrxq) == NULL) { 8952e1cf57SMatthew Dillon tsleep(&nmp->nm_rxstate, 0, "nfsidl", 0); 90edb90c22SMatthew Dillon } else { 91edb90c22SMatthew Dillon /* 92edb90c22SMatthew Dillon * This can happen during shutdown, we don't 93edb90c22SMatthew Dillon * want to hardloop. 94edb90c22SMatthew Dillon */ 95edb90c22SMatthew Dillon error = nfs_reply(nmp, NULL); 96edb90c22SMatthew Dillon if (error && error != EWOULDBLOCK) { 97edb90c22SMatthew Dillon tsleep(&nmp->nm_rxstate, 0, 98edb90c22SMatthew Dillon "nfsxxx", hz / 10); 99edb90c22SMatthew Dillon } 100edb90c22SMatthew Dillon } 10152e1cf57SMatthew Dillon continue; 10252e1cf57SMatthew Dillon } 10352e1cf57SMatthew Dillon if (nmp->nm_rxstate != NFSSVC_PENDING) 10452e1cf57SMatthew Dillon break; 10552e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_WAITING; 10652e1cf57SMatthew Dillon 107edb90c22SMatthew Dillon /* 108edb90c22SMatthew Dillon * Process requests which have received replies. Only 109edb90c22SMatthew Dillon * process the post-reply states. If we get EINPROGRESS 110edb90c22SMatthew Dillon * it means the request went back to an auth or retransmit 111edb90c22SMatthew Dillon * state and we let the iod_writer thread deal with it. 112edb90c22SMatthew Dillon * 113f8565b0fSMatthew Dillon * Any lock on the request is strictly temporary due to 114f8565b0fSMatthew Dillon * MP races (XXX). 115f8565b0fSMatthew Dillon * 116edb90c22SMatthew Dillon * If the request completes we run the info->done call 117edb90c22SMatthew Dillon * to finish up the I/O. 118edb90c22SMatthew Dillon */ 119edb90c22SMatthew Dillon while ((req = TAILQ_FIRST(&nmp->nm_reqrxq)) != NULL) { 120f8565b0fSMatthew Dillon if (req->r_flags & R_LOCKED) { 121f8565b0fSMatthew Dillon while (req->r_flags & R_LOCKED) { 122f8565b0fSMatthew Dillon req->r_flags |= R_WANTED; 123f8565b0fSMatthew Dillon tsleep(req, 0, "nfstrac", 0); 124f8565b0fSMatthew Dillon } 125f8565b0fSMatthew Dillon continue; 126f8565b0fSMatthew Dillon } 127edb90c22SMatthew Dillon TAILQ_REMOVE(&nmp->nm_reqrxq, req, r_chain); 128f8565b0fSMatthew Dillon crit_exit(); 129edb90c22SMatthew Dillon info = req->r_info; 130edb90c22SMatthew Dillon KKASSERT(info); 131edb90c22SMatthew Dillon info->error = nfs_request(info, 132edb90c22SMatthew Dillon NFSM_STATE_PROCESSREPLY, 133edb90c22SMatthew Dillon NFSM_STATE_DONE); 134edb90c22SMatthew Dillon if (info->error == EINPROGRESS) { 135edb90c22SMatthew Dillon kprintf("rxq: move info %p back to txq\n", info); 136edb90c22SMatthew Dillon TAILQ_INSERT_TAIL(&nmp->nm_reqtxq, req, r_chain); 137edb90c22SMatthew Dillon nfssvc_iod_writer_wakeup(nmp); 138edb90c22SMatthew Dillon } else { 139f8565b0fSMatthew Dillon atomic_subtract_int(&nmp->nm_bioqlen, 1); 140edb90c22SMatthew Dillon info->done(info); 141edb90c22SMatthew Dillon } 142f8565b0fSMatthew Dillon crit_enter(); 143edb90c22SMatthew Dillon } 14452e1cf57SMatthew Dillon } 145f8565b0fSMatthew Dillon crit_exit(); 14652e1cf57SMatthew Dillon nmp->nm_rxthread = NULL; 14752e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_DONE; 14852e1cf57SMatthew Dillon wakeup(&nmp->nm_rxthread); 14952e1cf57SMatthew Dillon } 15052e1cf57SMatthew Dillon 15152e1cf57SMatthew Dillon /* 15252e1cf57SMatthew Dillon * The writer sits on the send side of the client's socket and 15352e1cf57SMatthew Dillon * does both the initial processing of BIOs and also transmission 15452e1cf57SMatthew Dillon * and retransmission of nfsreq's. 155edb90c22SMatthew Dillon * 156edb90c22SMatthew Dillon * The writer processes both new BIOs from nm_bioq and retransmit 157edb90c22SMatthew Dillon * or state machine jumpbacks from nm_reqtxq 15852e1cf57SMatthew Dillon */ 15952e1cf57SMatthew Dillon void 16052e1cf57SMatthew Dillon nfssvc_iod_writer(void *arg) 16152e1cf57SMatthew Dillon { 16252e1cf57SMatthew Dillon struct nfsmount *nmp = arg; 16352e1cf57SMatthew Dillon struct bio *bio; 164edb90c22SMatthew Dillon struct nfsreq *req; 16552e1cf57SMatthew Dillon struct vnode *vp; 166edb90c22SMatthew Dillon nfsm_info_t info; 16752e1cf57SMatthew Dillon 16852e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_INIT) 16952e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_PENDING; 170f8565b0fSMatthew Dillon crit_enter(); 17152e1cf57SMatthew Dillon for (;;) { 17252e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_WAITING) { 17352e1cf57SMatthew Dillon tsleep(&nmp->nm_txstate, 0, "nfsidl", 0); 17452e1cf57SMatthew Dillon continue; 17552e1cf57SMatthew Dillon } 17652e1cf57SMatthew Dillon if (nmp->nm_txstate != NFSSVC_PENDING) 17752e1cf57SMatthew Dillon break; 17852e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_WAITING; 17952e1cf57SMatthew Dillon 180cc7d050eSMatthew Dillon /* 181cc7d050eSMatthew Dillon * Eep, we could blow out the mbuf allocator if we just 182cc7d050eSMatthew Dillon * did everything the kernel wanted us to do. 183cc7d050eSMatthew Dillon */ 184f8565b0fSMatthew Dillon while ((bio = TAILQ_FIRST(&nmp->nm_bioq)) != NULL) { 185*b9a7a2bdSMatthew Dillon if (nmp->nm_reqqlen > nfs_maxasyncbio) 186cc7d050eSMatthew Dillon break; 18752e1cf57SMatthew Dillon TAILQ_REMOVE(&nmp->nm_bioq, bio, bio_act); 18852e1cf57SMatthew Dillon vp = bio->bio_driver_info; 189f8565b0fSMatthew Dillon crit_exit(); 190edb90c22SMatthew Dillon nfs_startio(vp, bio, NULL); 191f8565b0fSMatthew Dillon crit_enter(); 192edb90c22SMatthew Dillon } 193edb90c22SMatthew Dillon 194edb90c22SMatthew Dillon /* 195edb90c22SMatthew Dillon * Process reauths & retransmits. If we get an EINPROGRESS 196edb90c22SMatthew Dillon * it means the state transitioned to WAITREPLY or later. 197edb90c22SMatthew Dillon * Otherwise the request completed (probably with an error 198edb90c22SMatthew Dillon * since we didn't get to a replied state). 199edb90c22SMatthew Dillon */ 200edb90c22SMatthew Dillon while ((req = TAILQ_FIRST(&nmp->nm_reqtxq)) != NULL) { 201edb90c22SMatthew Dillon TAILQ_REMOVE(&nmp->nm_reqtxq, req, r_chain); 202edb90c22SMatthew Dillon info = req->r_info; 203edb90c22SMatthew Dillon KKASSERT(info); 204f8565b0fSMatthew Dillon crit_exit(); 205edb90c22SMatthew Dillon info->error = nfs_request(info, 206edb90c22SMatthew Dillon NFSM_STATE_AUTH, 207edb90c22SMatthew Dillon NFSM_STATE_WAITREPLY); 208f8565b0fSMatthew Dillon crit_enter(); 209edb90c22SMatthew Dillon if (info->error == EINPROGRESS) { 210f8565b0fSMatthew Dillon ; 211edb90c22SMatthew Dillon } else { 212f8565b0fSMatthew Dillon atomic_subtract_int(&nmp->nm_bioqlen, 1); 213edb90c22SMatthew Dillon info->done(info); 214edb90c22SMatthew Dillon } 21552e1cf57SMatthew Dillon } 21652e1cf57SMatthew Dillon } 217f8565b0fSMatthew Dillon crit_exit(); 21852e1cf57SMatthew Dillon nmp->nm_txthread = NULL; 21952e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_DONE; 22052e1cf57SMatthew Dillon wakeup(&nmp->nm_txthread); 22152e1cf57SMatthew Dillon } 22252e1cf57SMatthew Dillon 22352e1cf57SMatthew Dillon void 22413ddc895SMatthew Dillon nfssvc_iod_stop1(struct nfsmount *nmp) 22552e1cf57SMatthew Dillon { 22613ddc895SMatthew Dillon crit_enter(); 22752e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_STOPPING; 22813ddc895SMatthew Dillon nmp->nm_rxstate = NFSSVC_STOPPING; 22913ddc895SMatthew Dillon crit_exit(); 23013ddc895SMatthew Dillon } 23113ddc895SMatthew Dillon 23213ddc895SMatthew Dillon void 23313ddc895SMatthew Dillon nfssvc_iod_stop2(struct nfsmount *nmp) 23413ddc895SMatthew Dillon { 23552e1cf57SMatthew Dillon wakeup(&nmp->nm_txstate); 23652e1cf57SMatthew Dillon while (nmp->nm_txthread) 237a63246d1SMatthew Dillon tsleep(&nmp->nm_txthread, 0, "nfssttx", hz*2); 23852e1cf57SMatthew Dillon wakeup(&nmp->nm_rxstate); 23952e1cf57SMatthew Dillon while (nmp->nm_rxthread) 240a63246d1SMatthew Dillon tsleep(&nmp->nm_rxthread, 0, "nfsstrx", hz*2); 24152e1cf57SMatthew Dillon } 24252e1cf57SMatthew Dillon 24352e1cf57SMatthew Dillon void 24452e1cf57SMatthew Dillon nfssvc_iod_writer_wakeup(struct nfsmount *nmp) 24552e1cf57SMatthew Dillon { 24652e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_WAITING) { 24752e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_PENDING; 24852e1cf57SMatthew Dillon wakeup(&nmp->nm_txstate); 24952e1cf57SMatthew Dillon } 25052e1cf57SMatthew Dillon } 251edb90c22SMatthew Dillon 252edb90c22SMatthew Dillon void 253edb90c22SMatthew Dillon nfssvc_iod_reader_wakeup(struct nfsmount *nmp) 254edb90c22SMatthew Dillon { 255edb90c22SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_WAITING) { 256edb90c22SMatthew Dillon nmp->nm_rxstate = NFSSVC_PENDING; 257edb90c22SMatthew Dillon wakeup(&nmp->nm_rxstate); 258edb90c22SMatthew Dillon } 259edb90c22SMatthew Dillon } 260