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> 59c9e9fb21SMatthew Dillon #include <sys/thread2.h> 6052e1cf57SMatthew Dillon #include <sys/mutex2.h> 61c9e9fb21SMatthew 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 75c9e9fb21SMatthew Dillon /* 76c9e9fb21SMatthew Dillon * nfs service connection reader thread 77c9e9fb21SMatthew 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*c6b43e93SMatthew Dillon lwkt_gettoken(&nmp->nm_token); 87c9e9fb21SMatthew Dillon 8852e1cf57SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_INIT) 8952e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_PENDING; 9052e1cf57SMatthew Dillon for (;;) { 9152e1cf57SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_WAITING) { 92edb90c22SMatthew Dillon if (TAILQ_FIRST(&nmp->nm_reqq) == NULL && 93edb90c22SMatthew Dillon TAILQ_FIRST(&nmp->nm_reqrxq) == NULL) { 9452e1cf57SMatthew Dillon tsleep(&nmp->nm_rxstate, 0, "nfsidl", 0); 95edb90c22SMatthew Dillon } else { 96edb90c22SMatthew Dillon /* 97edb90c22SMatthew Dillon * This can happen during shutdown, we don't 98edb90c22SMatthew Dillon * want to hardloop. 99edb90c22SMatthew Dillon */ 100edb90c22SMatthew Dillon error = nfs_reply(nmp, NULL); 101edb90c22SMatthew Dillon if (error && error != EWOULDBLOCK) { 102edb90c22SMatthew Dillon tsleep(&nmp->nm_rxstate, 0, 103edb90c22SMatthew Dillon "nfsxxx", hz / 10); 104edb90c22SMatthew Dillon } 105edb90c22SMatthew Dillon } 10652e1cf57SMatthew Dillon continue; 10752e1cf57SMatthew Dillon } 10852e1cf57SMatthew Dillon if (nmp->nm_rxstate != NFSSVC_PENDING) 10952e1cf57SMatthew Dillon break; 11052e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_WAITING; 11152e1cf57SMatthew Dillon 112edb90c22SMatthew Dillon /* 113edb90c22SMatthew Dillon * Process requests which have received replies. Only 114edb90c22SMatthew Dillon * process the post-reply states. If we get EINPROGRESS 115edb90c22SMatthew Dillon * it means the request went back to an auth or retransmit 116edb90c22SMatthew Dillon * state and we let the iod_writer thread deal with it. 117edb90c22SMatthew Dillon * 118f8565b0fSMatthew Dillon * Any lock on the request is strictly temporary due to 119f8565b0fSMatthew Dillon * MP races (XXX). 120f8565b0fSMatthew Dillon * 121edb90c22SMatthew Dillon * If the request completes we run the info->done call 122edb90c22SMatthew Dillon * to finish up the I/O. 123edb90c22SMatthew Dillon */ 124edb90c22SMatthew Dillon while ((req = TAILQ_FIRST(&nmp->nm_reqrxq)) != NULL) { 125f8565b0fSMatthew Dillon if (req->r_flags & R_LOCKED) { 126f8565b0fSMatthew Dillon while (req->r_flags & R_LOCKED) { 127f8565b0fSMatthew Dillon req->r_flags |= R_WANTED; 128f8565b0fSMatthew Dillon tsleep(req, 0, "nfstrac", 0); 129f8565b0fSMatthew Dillon } 130f8565b0fSMatthew Dillon continue; 131f8565b0fSMatthew Dillon } 132edb90c22SMatthew Dillon TAILQ_REMOVE(&nmp->nm_reqrxq, req, r_chain); 133edb90c22SMatthew Dillon info = req->r_info; 134edb90c22SMatthew Dillon KKASSERT(info); 135edb90c22SMatthew Dillon info->error = nfs_request(info, 136edb90c22SMatthew Dillon NFSM_STATE_PROCESSREPLY, 137edb90c22SMatthew Dillon NFSM_STATE_DONE); 138edb90c22SMatthew Dillon if (info->error == EINPROGRESS) { 139edb90c22SMatthew Dillon kprintf("rxq: move info %p back to txq\n", info); 140edb90c22SMatthew Dillon TAILQ_INSERT_TAIL(&nmp->nm_reqtxq, req, r_chain); 141edb90c22SMatthew Dillon nfssvc_iod_writer_wakeup(nmp); 142edb90c22SMatthew Dillon } else { 143f8565b0fSMatthew Dillon atomic_subtract_int(&nmp->nm_bioqlen, 1); 144edb90c22SMatthew Dillon info->done(info); 145edb90c22SMatthew Dillon } 146edb90c22SMatthew Dillon } 14752e1cf57SMatthew Dillon } 14852e1cf57SMatthew Dillon nmp->nm_rxthread = NULL; 14952e1cf57SMatthew Dillon nmp->nm_rxstate = NFSSVC_DONE; 150*c6b43e93SMatthew Dillon 151*c6b43e93SMatthew Dillon lwkt_reltoken(&nmp->nm_token); 15252e1cf57SMatthew Dillon wakeup(&nmp->nm_rxthread); 15352e1cf57SMatthew Dillon } 15452e1cf57SMatthew Dillon 15552e1cf57SMatthew Dillon /* 156c9e9fb21SMatthew Dillon * nfs service connection writer thread 157c9e9fb21SMatthew Dillon * 15852e1cf57SMatthew Dillon * The writer sits on the send side of the client's socket and 15952e1cf57SMatthew Dillon * does both the initial processing of BIOs and also transmission 16052e1cf57SMatthew Dillon * and retransmission of nfsreq's. 161edb90c22SMatthew Dillon * 162edb90c22SMatthew Dillon * The writer processes both new BIOs from nm_bioq and retransmit 163edb90c22SMatthew Dillon * or state machine jumpbacks from nm_reqtxq 16452e1cf57SMatthew Dillon */ 16552e1cf57SMatthew Dillon void 16652e1cf57SMatthew Dillon nfssvc_iod_writer(void *arg) 16752e1cf57SMatthew Dillon { 16852e1cf57SMatthew Dillon struct nfsmount *nmp = arg; 16952e1cf57SMatthew Dillon struct bio *bio; 170edb90c22SMatthew Dillon struct nfsreq *req; 17152e1cf57SMatthew Dillon struct vnode *vp; 172edb90c22SMatthew Dillon nfsm_info_t info; 17352e1cf57SMatthew Dillon 174*c6b43e93SMatthew Dillon lwkt_gettoken(&nmp->nm_token); 175c9e9fb21SMatthew Dillon 17652e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_INIT) 17752e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_PENDING; 178c9e9fb21SMatthew Dillon 17952e1cf57SMatthew Dillon for (;;) { 18052e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_WAITING) { 18152e1cf57SMatthew Dillon tsleep(&nmp->nm_txstate, 0, "nfsidl", 0); 18252e1cf57SMatthew Dillon continue; 18352e1cf57SMatthew Dillon } 18452e1cf57SMatthew Dillon if (nmp->nm_txstate != NFSSVC_PENDING) 18552e1cf57SMatthew Dillon break; 18652e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_WAITING; 18752e1cf57SMatthew Dillon 188cc7d050eSMatthew Dillon /* 189cc7d050eSMatthew Dillon * Eep, we could blow out the mbuf allocator if we just 190cc7d050eSMatthew Dillon * did everything the kernel wanted us to do. 191cc7d050eSMatthew Dillon */ 192f8565b0fSMatthew Dillon while ((bio = TAILQ_FIRST(&nmp->nm_bioq)) != NULL) { 193b9a7a2bdSMatthew Dillon if (nmp->nm_reqqlen > nfs_maxasyncbio) 194cc7d050eSMatthew Dillon break; 19552e1cf57SMatthew Dillon TAILQ_REMOVE(&nmp->nm_bioq, bio, bio_act); 19652e1cf57SMatthew Dillon vp = bio->bio_driver_info; 197edb90c22SMatthew Dillon nfs_startio(vp, bio, NULL); 198edb90c22SMatthew Dillon } 199edb90c22SMatthew Dillon 200edb90c22SMatthew Dillon /* 201edb90c22SMatthew Dillon * Process reauths & retransmits. If we get an EINPROGRESS 202edb90c22SMatthew Dillon * it means the state transitioned to WAITREPLY or later. 203edb90c22SMatthew Dillon * Otherwise the request completed (probably with an error 204edb90c22SMatthew Dillon * since we didn't get to a replied state). 205edb90c22SMatthew Dillon */ 206edb90c22SMatthew Dillon while ((req = TAILQ_FIRST(&nmp->nm_reqtxq)) != NULL) { 207edb90c22SMatthew Dillon TAILQ_REMOVE(&nmp->nm_reqtxq, req, r_chain); 208edb90c22SMatthew Dillon info = req->r_info; 209edb90c22SMatthew Dillon KKASSERT(info); 210edb90c22SMatthew Dillon info->error = nfs_request(info, 211edb90c22SMatthew Dillon NFSM_STATE_AUTH, 212edb90c22SMatthew Dillon NFSM_STATE_WAITREPLY); 213edb90c22SMatthew Dillon if (info->error == EINPROGRESS) { 214f8565b0fSMatthew Dillon ; 215edb90c22SMatthew Dillon } else { 216f8565b0fSMatthew Dillon atomic_subtract_int(&nmp->nm_bioqlen, 1); 217edb90c22SMatthew Dillon info->done(info); 218edb90c22SMatthew Dillon } 21952e1cf57SMatthew Dillon } 22052e1cf57SMatthew Dillon } 22152e1cf57SMatthew Dillon nmp->nm_txthread = NULL; 22252e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_DONE; 223*c6b43e93SMatthew Dillon lwkt_reltoken(&nmp->nm_token); 22452e1cf57SMatthew Dillon wakeup(&nmp->nm_txthread); 22552e1cf57SMatthew Dillon } 22652e1cf57SMatthew Dillon 22752e1cf57SMatthew Dillon void 22813ddc895SMatthew Dillon nfssvc_iod_stop1(struct nfsmount *nmp) 22952e1cf57SMatthew Dillon { 23052e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_STOPPING; 23113ddc895SMatthew Dillon nmp->nm_rxstate = NFSSVC_STOPPING; 23213ddc895SMatthew Dillon } 23313ddc895SMatthew Dillon 23413ddc895SMatthew Dillon void 23513ddc895SMatthew Dillon nfssvc_iod_stop2(struct nfsmount *nmp) 23613ddc895SMatthew Dillon { 23752e1cf57SMatthew Dillon wakeup(&nmp->nm_txstate); 23852e1cf57SMatthew Dillon while (nmp->nm_txthread) 239a63246d1SMatthew Dillon tsleep(&nmp->nm_txthread, 0, "nfssttx", hz*2); 24052e1cf57SMatthew Dillon wakeup(&nmp->nm_rxstate); 24152e1cf57SMatthew Dillon while (nmp->nm_rxthread) 242a63246d1SMatthew Dillon tsleep(&nmp->nm_rxthread, 0, "nfsstrx", hz*2); 24352e1cf57SMatthew Dillon } 24452e1cf57SMatthew Dillon 24552e1cf57SMatthew Dillon void 24652e1cf57SMatthew Dillon nfssvc_iod_writer_wakeup(struct nfsmount *nmp) 24752e1cf57SMatthew Dillon { 24852e1cf57SMatthew Dillon if (nmp->nm_txstate == NFSSVC_WAITING) { 24952e1cf57SMatthew Dillon nmp->nm_txstate = NFSSVC_PENDING; 25052e1cf57SMatthew Dillon wakeup(&nmp->nm_txstate); 25152e1cf57SMatthew Dillon } 25252e1cf57SMatthew Dillon } 253edb90c22SMatthew Dillon 254edb90c22SMatthew Dillon void 255edb90c22SMatthew Dillon nfssvc_iod_reader_wakeup(struct nfsmount *nmp) 256edb90c22SMatthew Dillon { 257edb90c22SMatthew Dillon if (nmp->nm_rxstate == NFSSVC_WAITING) { 258edb90c22SMatthew Dillon nmp->nm_rxstate = NFSSVC_PENDING; 259edb90c22SMatthew Dillon wakeup(&nmp->nm_rxstate); 260edb90c22SMatthew Dillon } 261edb90c22SMatthew Dillon } 262