nlm_prot_impl.c revision 177685
1177633Sdfr/*- 2177633Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 3177633Sdfr * Authors: Doug Rabson <dfr@rabson.org> 4177633Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 5177633Sdfr * 6177633Sdfr * Redistribution and use in source and binary forms, with or without 7177633Sdfr * modification, are permitted provided that the following conditions 8177633Sdfr * are met: 9177633Sdfr * 1. Redistributions of source code must retain the above copyright 10177633Sdfr * notice, this list of conditions and the following disclaimer. 11177633Sdfr * 2. Redistributions in binary form must reproduce the above copyright 12177633Sdfr * notice, this list of conditions and the following disclaimer in the 13177633Sdfr * documentation and/or other materials provided with the distribution. 14177633Sdfr * 15177633Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16177633Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17177633Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18177633Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19177633Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20177633Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21177633Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22177633Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23177633Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24177633Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25177633Sdfr * SUCH DAMAGE. 26177633Sdfr */ 27177633Sdfr 28177633Sdfr#include "opt_inet6.h" 29177633Sdfr 30177633Sdfr#include <sys/cdefs.h> 31177633Sdfr__FBSDID("$FreeBSD: head/sys/nlm/nlm_prot_impl.c 177685 2008-03-28 09:50:32Z dfr $"); 32177633Sdfr 33177633Sdfr#include <sys/param.h> 34177633Sdfr#include <sys/fcntl.h> 35177633Sdfr#include <sys/kernel.h> 36177633Sdfr#include <sys/lockf.h> 37177633Sdfr#include <sys/malloc.h> 38177633Sdfr#include <sys/mount.h> 39177685Sdfr#if __FreeBSD_version >= 700000 40177633Sdfr#include <sys/priv.h> 41177685Sdfr#endif 42177633Sdfr#include <sys/proc.h> 43177633Sdfr#include <sys/socket.h> 44177633Sdfr#include <sys/socketvar.h> 45177633Sdfr#include <sys/syscall.h> 46177633Sdfr#include <sys/sysctl.h> 47177633Sdfr#include <sys/sysent.h> 48177633Sdfr#include <sys/sysproto.h> 49177633Sdfr#include <sys/systm.h> 50177633Sdfr#include <sys/taskqueue.h> 51177633Sdfr#include <sys/unistd.h> 52177633Sdfr#include <sys/vnode.h> 53177633Sdfr 54177685Sdfr#include <nlm/nlm_prot.h> 55177685Sdfr#include <nlm/sm_inter.h> 56177685Sdfr#include <nlm/nlm.h> 57177633Sdfr#include <rpc/rpc_com.h> 58177633Sdfr#include <rpc/rpcb_prot.h> 59177633Sdfr 60177633SdfrMALLOC_DEFINE(M_NLM, "NLM", "Network Lock Manager"); 61177633Sdfr 62177633Sdfr/* 63177633Sdfr * If a host is inactive (and holds no locks) for this amount of 64177633Sdfr * seconds, we consider it idle and stop tracking it. 65177633Sdfr */ 66177633Sdfr#define NLM_IDLE_TIMEOUT 30 67177633Sdfr 68177633Sdfr/* 69177633Sdfr * We check the host list for idle every few seconds. 70177633Sdfr */ 71177633Sdfr#define NLM_IDLE_PERIOD 5 72177633Sdfr 73177633Sdfr/* 74177633Sdfr * Support for sysctl vfs.nlm.sysid 75177633Sdfr */ 76177633SdfrSYSCTL_NODE(_vfs, OID_AUTO, nlm, CTLFLAG_RW, NULL, "Network Lock Manager"); 77177633SdfrSYSCTL_NODE(_vfs_nlm, OID_AUTO, sysid, CTLFLAG_RW, NULL, ""); 78177633Sdfr 79177633Sdfr/* 80177633Sdfr * Syscall hooks 81177633Sdfr */ 82177633Sdfrstatic int nlm_syscall_offset = SYS_nlm_syscall; 83177633Sdfrstatic struct sysent nlm_syscall_prev_sysent; 84177685Sdfr#if __FreeBSD_version < 700000 85177685Sdfrstatic struct sysent nlm_syscall_sysent = { 86177685Sdfr (sizeof(struct nlm_syscall_args) / sizeof(register_t)) | SYF_MPSAFE, 87177685Sdfr (sy_call_t *) nlm_syscall 88177685Sdfr}; 89177685Sdfr#else 90177633SdfrMAKE_SYSENT(nlm_syscall); 91177685Sdfr#endif 92177633Sdfrstatic bool_t nlm_syscall_registered = FALSE; 93177633Sdfr 94177633Sdfr/* 95177633Sdfr * Debug level passed in from userland. We also support a sysctl hook 96177633Sdfr * so that it can be changed on a live system. 97177633Sdfr */ 98177633Sdfrstatic int nlm_debug_level; 99177633SdfrSYSCTL_INT(_debug, OID_AUTO, nlm_debug, CTLFLAG_RW, &nlm_debug_level, 0, ""); 100177633Sdfr 101177633Sdfr/* 102177633Sdfr * Grace period handling. The value of nlm_grace_threshold is the 103177633Sdfr * value of time_uptime after which we are serving requests normally. 104177633Sdfr */ 105177633Sdfrstatic time_t nlm_grace_threshold; 106177633Sdfr 107177633Sdfr/* 108177633Sdfr * We check for idle hosts if time_uptime is greater than 109177633Sdfr * nlm_next_idle_check, 110177633Sdfr */ 111177633Sdfrstatic time_t nlm_next_idle_check; 112177633Sdfr 113177633Sdfr/* 114177633Sdfr * A socket to use for RPC - shared by all IPv4 RPC clients. 115177633Sdfr */ 116177633Sdfrstatic struct socket *nlm_socket; 117177633Sdfr 118177633Sdfr#ifdef INET6 119177633Sdfr 120177633Sdfr/* 121177633Sdfr * A socket to use for RPC - shared by all IPv6 RPC clients. 122177633Sdfr */ 123177633Sdfrstatic struct socket *nlm_socket6; 124177633Sdfr 125177633Sdfr#endif 126177633Sdfr 127177633Sdfr/* 128177633Sdfr * An RPC client handle that can be used to communicate with the local 129177633Sdfr * NSM. 130177633Sdfr */ 131177633Sdfrstatic CLIENT *nlm_nsm; 132177633Sdfr 133177633Sdfr/* 134177633Sdfr * An RPC client handle that can be used to communicate with the 135177633Sdfr * userland part of lockd. 136177633Sdfr */ 137177633Sdfrstatic CLIENT *nlm_lockd; 138177633Sdfr 139177633Sdfr/* 140177633Sdfr * Locks: 141177633Sdfr * (l) locked by nh_lock 142177633Sdfr * (s) only accessed via server RPC which is single threaded 143177633Sdfr * (c) const until freeing 144177633Sdfr */ 145177633Sdfr 146177633Sdfr/* 147177633Sdfr * A pending asynchronous lock request, stored on the nc_pending list 148177633Sdfr * of the NLM host. 149177633Sdfr */ 150177633Sdfrstruct nlm_async_lock { 151177633Sdfr TAILQ_ENTRY(nlm_async_lock) af_link; /* (l) host's list of locks */ 152177633Sdfr struct task af_task; /* (c) async callback details */ 153177633Sdfr void *af_cookie; /* (l) lock manager cancel token */ 154177633Sdfr struct vnode *af_vp; /* (l) vnode to lock */ 155177633Sdfr struct flock af_fl; /* (c) lock details */ 156177633Sdfr struct nlm_host *af_host; /* (c) host which is locking */ 157177633Sdfr nlm4_testargs af_granted; /* (c) notification details */ 158177633Sdfr}; 159177633SdfrTAILQ_HEAD(nlm_async_lock_list, nlm_async_lock); 160177633Sdfr 161177633Sdfr/* 162177633Sdfr * NLM host. 163177633Sdfr */ 164177633Sdfrstruct nlm_host { 165177633Sdfr struct mtx nh_lock; 166177633Sdfr TAILQ_ENTRY(nlm_host) nh_link; /* (s) global list of hosts */ 167177633Sdfr char *nh_caller_name; /* (c) printable name of host */ 168177633Sdfr uint32_t nh_sysid; /* (c) our allocaed system ID */ 169177633Sdfr char nh_sysid_string[10]; /* (c) string rep. of sysid */ 170177633Sdfr struct sockaddr_storage nh_addr; /* (s) remote address of host */ 171177633Sdfr CLIENT *nh_rpc; /* (s) RPC handle to send to host */ 172177633Sdfr rpcvers_t nh_vers; /* (s) NLM version of host */ 173177633Sdfr int nh_state; /* (s) last seen NSM state of host */ 174177633Sdfr bool_t nh_monitored; /* (s) TRUE if local NSM is monitoring */ 175177633Sdfr time_t nh_idle_timeout; /* (s) Time at which host is idle */ 176177633Sdfr struct sysctl_ctx_list nh_sysctl; /* (c) vfs.nlm.sysid nodes */ 177177633Sdfr struct nlm_async_lock_list nh_pending; /* (l) pending async locks */ 178177633Sdfr struct nlm_async_lock_list nh_finished; /* (l) finished async locks */ 179177633Sdfr}; 180177633SdfrTAILQ_HEAD(nlm_host_list, nlm_host); 181177633Sdfr 182177633Sdfrstatic struct nlm_host_list nlm_hosts; 183177633Sdfrstatic uint32_t nlm_next_sysid = 1; 184177633Sdfr 185177633Sdfrstatic void nlm_host_unmonitor(struct nlm_host *); 186177633Sdfr 187177633Sdfr/**********************************************************************/ 188177633Sdfr 189177633Sdfr/* 190177633Sdfr * Initialise NLM globals. 191177633Sdfr */ 192177633Sdfrstatic void 193177633Sdfrnlm_init(void *dummy) 194177633Sdfr{ 195177633Sdfr int error; 196177633Sdfr 197177633Sdfr TAILQ_INIT(&nlm_hosts); 198177633Sdfr 199177633Sdfr error = syscall_register(&nlm_syscall_offset, &nlm_syscall_sysent, 200177633Sdfr &nlm_syscall_prev_sysent); 201177633Sdfr if (error) 202177633Sdfr printf("Can't register NLM syscall\n"); 203177633Sdfr else 204177633Sdfr nlm_syscall_registered = TRUE; 205177633Sdfr} 206177633SdfrSYSINIT(nlm_init, SI_SUB_LOCK, SI_ORDER_FIRST, nlm_init, NULL); 207177633Sdfr 208177633Sdfrstatic void 209177633Sdfrnlm_uninit(void *dummy) 210177633Sdfr{ 211177633Sdfr 212177633Sdfr if (nlm_syscall_registered) 213177633Sdfr syscall_deregister(&nlm_syscall_offset, 214177633Sdfr &nlm_syscall_prev_sysent); 215177633Sdfr} 216177633SdfrSYSUNINIT(nlm_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, nlm_uninit, NULL); 217177633Sdfr 218177633Sdfr/* 219177633Sdfr * Copy a struct netobj. 220177633Sdfr */ 221177633Sdfrvoid 222177633Sdfrnlm_copy_netobj(struct netobj *dst, struct netobj *src, 223177633Sdfr struct malloc_type *type) 224177633Sdfr{ 225177633Sdfr 226177633Sdfr dst->n_len = src->n_len; 227177633Sdfr dst->n_bytes = malloc(src->n_len, type, M_WAITOK); 228177633Sdfr memcpy(dst->n_bytes, src->n_bytes, src->n_len); 229177633Sdfr} 230177633Sdfr 231177633Sdfr/* 232177633Sdfr * Create an RPC client handle for the given (address,prog,vers) 233177633Sdfr * triple using UDP. 234177633Sdfr */ 235177633Sdfrstatic CLIENT * 236177633Sdfrnlm_get_rpc(struct sockaddr *sa, rpcprog_t prog, rpcvers_t vers) 237177633Sdfr{ 238177633Sdfr const char *wchan = "nlmrcv"; 239177633Sdfr const char* protofmly; 240177633Sdfr struct sockaddr_storage ss; 241177633Sdfr struct socket *so; 242177633Sdfr CLIENT *rpcb; 243177633Sdfr struct timeval timo; 244177633Sdfr RPCB parms; 245177633Sdfr char *uaddr; 246177633Sdfr enum clnt_stat stat; 247177633Sdfr int rpcvers; 248177633Sdfr 249177633Sdfr /* 250177633Sdfr * First we need to contact the remote RPCBIND service to find 251177633Sdfr * the right port. 252177633Sdfr */ 253177633Sdfr memcpy(&ss, sa, sa->sa_len); 254177633Sdfr switch (ss.ss_family) { 255177633Sdfr case AF_INET: 256177633Sdfr ((struct sockaddr_in *)&ss)->sin_port = htons(111); 257177633Sdfr protofmly = "inet"; 258177633Sdfr so = nlm_socket; 259177633Sdfr break; 260177633Sdfr 261177633Sdfr#ifdef INET6 262177633Sdfr case AF_INET6: 263177633Sdfr ((struct sockaddr_in6 *)&ss)->sin6_port = htons(111); 264177633Sdfr protofmly = "inet6"; 265177633Sdfr so = nlm_socket6; 266177633Sdfr break; 267177633Sdfr#endif 268177633Sdfr 269177633Sdfr default: 270177633Sdfr /* 271177633Sdfr * Unsupported address family - fail. 272177633Sdfr */ 273177633Sdfr return (NULL); 274177633Sdfr } 275177633Sdfr 276177633Sdfr rpcb = clnt_dg_create(so, (struct sockaddr *)&ss, 277177633Sdfr RPCBPROG, RPCBVERS4, 0, 0); 278177633Sdfr if (!rpcb) 279177633Sdfr return (NULL); 280177633Sdfr 281177633Sdfr parms.r_prog = prog; 282177633Sdfr parms.r_vers = vers; 283177633Sdfr parms.r_netid = "udp"; 284177633Sdfr parms.r_addr = ""; 285177633Sdfr parms.r_owner = ""; 286177633Sdfr 287177633Sdfr /* 288177633Sdfr * Use the default timeout. 289177633Sdfr */ 290177633Sdfr timo.tv_sec = 25; 291177633Sdfr timo.tv_usec = 0; 292177633Sdfragain: 293177633Sdfr uaddr = NULL; 294177633Sdfr stat = CLNT_CALL(rpcb, (rpcprog_t) RPCBPROC_GETADDR, 295177633Sdfr (xdrproc_t) xdr_rpcb, &parms, 296177633Sdfr (xdrproc_t) xdr_wrapstring, &uaddr, timo); 297177633Sdfr if (stat == RPC_PROGVERSMISMATCH) { 298177633Sdfr /* 299177633Sdfr * Try RPCBIND version 3 if we haven't already. 300177633Sdfr * 301177633Sdfr * XXX fall back to portmap? 302177633Sdfr */ 303177633Sdfr CLNT_CONTROL(rpcb, CLGET_VERS, &rpcvers); 304177633Sdfr if (rpcvers == RPCBVERS4) { 305177633Sdfr rpcvers = RPCBVERS; 306177633Sdfr CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers); 307177633Sdfr goto again; 308177633Sdfr } 309177633Sdfr } 310177633Sdfr 311177633Sdfr if (stat == RPC_SUCCESS) { 312177633Sdfr /* 313177633Sdfr * We have a reply from the remote RPCBIND - turn it into an 314177633Sdfr * appropriate address and make a new client that can talk to 315177633Sdfr * the remote NLM. 316177633Sdfr * 317177633Sdfr * XXX fixup IPv6 scope ID. 318177633Sdfr */ 319177633Sdfr struct netbuf *a; 320177633Sdfr a = __rpc_uaddr2taddr_af(ss.ss_family, uaddr); 321177633Sdfr memcpy(&ss, a->buf, a->len); 322177633Sdfr free(a->buf, M_RPC); 323177633Sdfr free(a, M_RPC); 324177633Sdfr xdr_free((xdrproc_t) xdr_wrapstring, &uaddr); 325177633Sdfr } else if (stat == RPC_PROGVERSMISMATCH) { 326177633Sdfr /* 327177633Sdfr * Try portmap. 328177633Sdfr */ 329177633Sdfr struct pmap mapping; 330177633Sdfr u_short port; 331177633Sdfr 332177633Sdfr rpcvers = PMAPVERS; 333177633Sdfr CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers); 334177633Sdfr 335177633Sdfr 336177633Sdfr mapping.pm_prog = parms.r_prog; 337177633Sdfr mapping.pm_vers = parms.r_vers; 338177633Sdfr mapping.pm_prot = IPPROTO_UDP; 339177633Sdfr mapping.pm_port = 0; 340177633Sdfr 341177633Sdfr stat = CLNT_CALL(rpcb, (rpcprog_t) PMAPPROC_GETPORT, 342177633Sdfr (xdrproc_t) xdr_pmap, &mapping, 343177633Sdfr (xdrproc_t) xdr_u_short, &port, timo); 344177633Sdfr 345177633Sdfr if (stat == RPC_SUCCESS) { 346177633Sdfr switch (ss.ss_family) { 347177633Sdfr case AF_INET: 348177633Sdfr ((struct sockaddr_in *)&ss)->sin_port = 349177633Sdfr htons(port); 350177633Sdfr break; 351177633Sdfr 352177633Sdfr#ifdef INET6 353177633Sdfr case AF_INET6: 354177633Sdfr ((struct sockaddr_in6 *)&ss)->sin6_port = 355177633Sdfr htons(port); 356177633Sdfr break; 357177633Sdfr#endif 358177633Sdfr } 359177633Sdfr } 360177633Sdfr } 361177633Sdfr if (stat != RPC_SUCCESS) { 362177633Sdfr printf("NLM: failed to contact remote rpcbind, stat = %d\n", 363177633Sdfr (int) stat); 364177633Sdfr return (NULL); 365177633Sdfr } 366177633Sdfr 367177633Sdfr /* 368177633Sdfr * Re-use the client we used to speak to rpcbind. 369177633Sdfr */ 370177633Sdfr CLNT_CONTROL(rpcb, CLSET_SVC_ADDR, &ss); 371177633Sdfr CLNT_CONTROL(rpcb, CLSET_PROG, &prog); 372177633Sdfr CLNT_CONTROL(rpcb, CLSET_VERS, &vers); 373177633Sdfr CLNT_CONTROL(rpcb, CLSET_WAITCHAN, &wchan); 374177633Sdfr rpcb->cl_auth = authunix_create(curthread->td_ucred); 375177633Sdfr 376177633Sdfr return (rpcb); 377177633Sdfr} 378177633Sdfr 379177633Sdfr/* 380177633Sdfr * This async callback after when an async lock request has been 381177633Sdfr * granted. We notify the host which initiated the request. 382177633Sdfr */ 383177633Sdfrstatic void 384177633Sdfrnlm_lock_callback(void *arg, int pending) 385177633Sdfr{ 386177633Sdfr struct nlm_async_lock *af = (struct nlm_async_lock *) arg; 387177633Sdfr 388177633Sdfr if (nlm_debug_level >= 2) 389177633Sdfr printf("NLM: async lock %p for %s (sysid %d) granted\n", 390177633Sdfr af, af->af_host->nh_caller_name, 391177633Sdfr af->af_host->nh_sysid); 392177633Sdfr 393177633Sdfr /* 394177633Sdfr * Send the results back to the host. 395177633Sdfr * 396177633Sdfr * Note: there is a possible race here with nlm_host_notify 397177633Sdfr * destroying teh RPC client. To avoid problems, the first 398177633Sdfr * thing nlm_host_notify does is to cancel pending async lock 399177633Sdfr * requests. 400177633Sdfr */ 401177633Sdfr if (af->af_host->nh_vers == NLM_VERS4) { 402177633Sdfr nlm4_granted_msg_4(&af->af_granted, 403177633Sdfr NULL, af->af_host->nh_rpc); 404177633Sdfr } else { 405177633Sdfr /* 406177633Sdfr * Back-convert to legacy protocol 407177633Sdfr */ 408177633Sdfr nlm_testargs granted; 409177633Sdfr granted.cookie = af->af_granted.cookie; 410177633Sdfr granted.exclusive = af->af_granted.exclusive; 411177633Sdfr granted.alock.caller_name = 412177633Sdfr af->af_granted.alock.caller_name; 413177633Sdfr granted.alock.fh = af->af_granted.alock.fh; 414177633Sdfr granted.alock.oh = af->af_granted.alock.oh; 415177633Sdfr granted.alock.svid = af->af_granted.alock.svid; 416177633Sdfr granted.alock.l_offset = 417177633Sdfr af->af_granted.alock.l_offset; 418177633Sdfr granted.alock.l_len = 419177633Sdfr af->af_granted.alock.l_len; 420177633Sdfr 421177633Sdfr nlm_granted_msg_1(&granted, 422177633Sdfr NULL, af->af_host->nh_rpc); 423177633Sdfr } 424177633Sdfr 425177633Sdfr /* 426177633Sdfr * Move this entry to the nh_finished list. Someone else will 427177633Sdfr * free it later - its too hard to do it here safely without 428177633Sdfr * racing with cancel. 429177633Sdfr * 430177633Sdfr * XXX possibly we should have a third "granted sent but not 431177633Sdfr * ack'ed" list so that we can re-send the granted message. 432177633Sdfr */ 433177633Sdfr mtx_lock(&af->af_host->nh_lock); 434177633Sdfr TAILQ_REMOVE(&af->af_host->nh_pending, af, af_link); 435177633Sdfr TAILQ_INSERT_TAIL(&af->af_host->nh_finished, af, af_link); 436177633Sdfr mtx_unlock(&af->af_host->nh_lock); 437177633Sdfr} 438177633Sdfr 439177633Sdfr/* 440177633Sdfr * Free an async lock request. The request must have been removed from 441177633Sdfr * any list. 442177633Sdfr */ 443177633Sdfrstatic void 444177633Sdfrnlm_free_async_lock(struct nlm_async_lock *af) 445177633Sdfr{ 446177633Sdfr /* 447177633Sdfr * Free an async lock. 448177633Sdfr */ 449177633Sdfr xdr_free((xdrproc_t) xdr_nlm4_testargs, &af->af_granted); 450177633Sdfr if (af->af_vp) 451177633Sdfr vrele(af->af_vp); 452177633Sdfr free(af, M_NLM); 453177633Sdfr} 454177633Sdfr 455177633Sdfr/* 456177633Sdfr * Cancel our async request - this must be called with 457177633Sdfr * af->nh_host->nh_lock held. This is slightly complicated by a 458177633Sdfr * potential race with our own callback. If we fail to cancel the 459177633Sdfr * lock, it must already have been granted - we make sure our async 460177633Sdfr * task has completed by calling taskqueue_drain in this case. 461177633Sdfr */ 462177633Sdfrstatic int 463177633Sdfrnlm_cancel_async_lock(struct nlm_async_lock *af) 464177633Sdfr{ 465177633Sdfr struct nlm_host *host = af->af_host; 466177633Sdfr int error; 467177633Sdfr 468177633Sdfr mtx_assert(&host->nh_lock, MA_OWNED); 469177633Sdfr 470177633Sdfr mtx_unlock(&host->nh_lock); 471177633Sdfr 472177633Sdfr error = VOP_ADVLOCKASYNC(af->af_vp, NULL, F_CANCEL, &af->af_fl, 473177633Sdfr F_REMOTE, NULL, &af->af_cookie); 474177633Sdfr 475177633Sdfr if (error) { 476177633Sdfr /* 477177633Sdfr * We failed to cancel - make sure our callback has 478177633Sdfr * completed before we continue. 479177633Sdfr */ 480177633Sdfr taskqueue_drain(taskqueue_thread, &af->af_task); 481177633Sdfr } 482177633Sdfr 483177633Sdfr mtx_lock(&host->nh_lock); 484177633Sdfr 485177633Sdfr if (!error) { 486177633Sdfr if (nlm_debug_level >= 2) 487177633Sdfr printf("NLM: async lock %p for %s (sysid %d) " 488177633Sdfr "cancelled\n", 489177633Sdfr af, host->nh_caller_name, host->nh_sysid); 490177633Sdfr 491177633Sdfr /* 492177633Sdfr * Remove from the nh_pending list and free now that 493177633Sdfr * we are safe from the callback. 494177633Sdfr */ 495177633Sdfr TAILQ_REMOVE(&host->nh_pending, af, af_link); 496177633Sdfr mtx_unlock(&host->nh_lock); 497177633Sdfr nlm_free_async_lock(af); 498177633Sdfr mtx_lock(&host->nh_lock); 499177633Sdfr } 500177633Sdfr 501177633Sdfr return (error); 502177633Sdfr} 503177633Sdfr 504177633Sdfrstatic void 505177633Sdfrnlm_free_finished_locks(struct nlm_host *host) 506177633Sdfr{ 507177633Sdfr struct nlm_async_lock *af; 508177633Sdfr 509177633Sdfr mtx_lock(&host->nh_lock); 510177633Sdfr while ((af = TAILQ_FIRST(&host->nh_finished)) != NULL) { 511177633Sdfr TAILQ_REMOVE(&host->nh_finished, af, af_link); 512177633Sdfr mtx_unlock(&host->nh_lock); 513177633Sdfr nlm_free_async_lock(af); 514177633Sdfr mtx_lock(&host->nh_lock); 515177633Sdfr } 516177633Sdfr mtx_unlock(&host->nh_lock); 517177633Sdfr} 518177633Sdfr 519177633Sdfr/* 520177633Sdfr * This is called when we receive a host state change 521177633Sdfr * notification. We unlock any active locks owned by the host. 522177633Sdfr */ 523177633Sdfrstatic void 524177633Sdfrnlm_host_notify(struct nlm_host *host, int newstate, bool_t destroy) 525177633Sdfr{ 526177633Sdfr struct nlm_async_lock *af; 527177633Sdfr 528177633Sdfr if (newstate) { 529177633Sdfr if (nlm_debug_level >= 1) 530177633Sdfr printf("NLM: host %s (sysid %d) rebooted, new " 531177633Sdfr "state is %d\n", 532177633Sdfr host->nh_caller_name, host->nh_sysid, newstate); 533177633Sdfr } 534177633Sdfr 535177633Sdfr /* 536177633Sdfr * Cancel any pending async locks for this host. 537177633Sdfr */ 538177633Sdfr mtx_lock(&host->nh_lock); 539177633Sdfr while ((af = TAILQ_FIRST(&host->nh_pending)) != NULL) { 540177633Sdfr /* 541177633Sdfr * nlm_cancel_async_lock will remove the entry from 542177633Sdfr * nh_pending and free it. 543177633Sdfr */ 544177633Sdfr nlm_cancel_async_lock(af); 545177633Sdfr } 546177633Sdfr mtx_unlock(&host->nh_lock); 547177633Sdfr nlm_free_finished_locks(host); 548177633Sdfr 549177633Sdfr /* 550177633Sdfr * The host just rebooted - trash its locks and forget any 551177633Sdfr * RPC client handle that we may have for it. 552177633Sdfr */ 553177633Sdfr lf_clearremotesys(host->nh_sysid); 554177633Sdfr if (host->nh_rpc) { 555177633Sdfr AUTH_DESTROY(host->nh_rpc->cl_auth); 556177633Sdfr CLNT_DESTROY(host->nh_rpc); 557177633Sdfr host->nh_rpc = NULL; 558177633Sdfr } 559177633Sdfr host->nh_state = newstate; 560177633Sdfr 561177633Sdfr /* 562177633Sdfr * Destroy the host if the caller believes that it won't be 563177633Sdfr * used again. This is safe enough - if we see the same name 564177633Sdfr * again, we will just create a new host. 565177633Sdfr */ 566177633Sdfr if (destroy) { 567177633Sdfr TAILQ_REMOVE(&nlm_hosts, host, nh_link); 568177633Sdfr mtx_destroy(&host->nh_lock); 569177633Sdfr sysctl_ctx_free(&host->nh_sysctl); 570177633Sdfr free(host->nh_caller_name, M_NLM); 571177633Sdfr free(host, M_NLM); 572177633Sdfr } 573177633Sdfr} 574177633Sdfr 575177633Sdfr/* 576177633Sdfr * Sysctl handler to count the number of locks for a sysid. 577177633Sdfr */ 578177633Sdfrstatic int 579177633Sdfrnlm_host_lock_count_sysctl(SYSCTL_HANDLER_ARGS) 580177633Sdfr{ 581177633Sdfr struct nlm_host *host; 582177633Sdfr int count; 583177633Sdfr 584177633Sdfr host = oidp->oid_arg1; 585177633Sdfr count = lf_countlocks(host->nh_sysid); 586177633Sdfr return sysctl_handle_int(oidp, &count, 0, req); 587177633Sdfr} 588177633Sdfr 589177633Sdfr/* 590177633Sdfr * Create a new NLM host. 591177633Sdfr */ 592177633Sdfrstatic struct nlm_host * 593177633Sdfrnlm_create_host(const char* caller_name) 594177633Sdfr{ 595177633Sdfr struct nlm_host *host; 596177633Sdfr struct sysctl_oid *oid; 597177633Sdfr 598177633Sdfr if (nlm_debug_level >= 1) 599177633Sdfr printf("NLM: new host %s (sysid %d)\n", 600177633Sdfr caller_name, nlm_next_sysid); 601177633Sdfr host = malloc(sizeof(struct nlm_host), M_NLM, M_WAITOK|M_ZERO); 602177633Sdfr mtx_init(&host->nh_lock, "nh_lock", NULL, MTX_DEF); 603177633Sdfr host->nh_caller_name = strdup(caller_name, M_NLM); 604177633Sdfr host->nh_sysid = nlm_next_sysid++; 605177633Sdfr snprintf(host->nh_sysid_string, sizeof(host->nh_sysid_string), 606177633Sdfr "%d", host->nh_sysid); 607177633Sdfr host->nh_rpc = NULL; 608177633Sdfr host->nh_vers = 0; 609177633Sdfr host->nh_state = 0; 610177633Sdfr host->nh_monitored = FALSE; 611177633Sdfr TAILQ_INIT(&host->nh_pending); 612177633Sdfr TAILQ_INIT(&host->nh_finished); 613177633Sdfr TAILQ_INSERT_TAIL(&nlm_hosts, host, nh_link); 614177633Sdfr 615177633Sdfr sysctl_ctx_init(&host->nh_sysctl); 616177633Sdfr oid = SYSCTL_ADD_NODE(&host->nh_sysctl, 617177633Sdfr SYSCTL_STATIC_CHILDREN(_vfs_nlm_sysid), 618177633Sdfr OID_AUTO, host->nh_sysid_string, CTLFLAG_RD, NULL, ""); 619177633Sdfr SYSCTL_ADD_STRING(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 620177633Sdfr "hostname", CTLFLAG_RD, host->nh_caller_name, 0, ""); 621177633Sdfr SYSCTL_ADD_INT(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 622177633Sdfr "version", CTLFLAG_RD, &host->nh_vers, 0, ""); 623177633Sdfr SYSCTL_ADD_INT(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 624177633Sdfr "monitored", CTLFLAG_RD, &host->nh_monitored, 0, ""); 625177633Sdfr SYSCTL_ADD_PROC(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 626177633Sdfr "lock_count", CTLTYPE_INT | CTLFLAG_RD, host, 0, 627177633Sdfr nlm_host_lock_count_sysctl, "I", ""); 628177633Sdfr 629177633Sdfr return (host); 630177633Sdfr} 631177633Sdfr 632177633Sdfr/* 633177633Sdfr * Return non-zero if the address parts of the two sockaddrs are the 634177633Sdfr * same. 635177633Sdfr */ 636177633Sdfrstatic int 637177633Sdfrnlm_compare_addr(const struct sockaddr *a, const struct sockaddr *b) 638177633Sdfr{ 639177633Sdfr const struct sockaddr_in *a4, *b4; 640177633Sdfr#ifdef INET6 641177633Sdfr const struct sockaddr_in6 *a6, *b6; 642177633Sdfr#endif 643177633Sdfr 644177633Sdfr if (a->sa_family != b->sa_family) 645177633Sdfr return (FALSE); 646177633Sdfr 647177633Sdfr switch (a->sa_family) { 648177633Sdfr case AF_INET: 649177633Sdfr a4 = (const struct sockaddr_in *) a; 650177633Sdfr b4 = (const struct sockaddr_in *) b; 651177633Sdfr return !memcmp(&a4->sin_addr, &b4->sin_addr, 652177633Sdfr sizeof(a4->sin_addr)); 653177633Sdfr#ifdef INET6 654177633Sdfr case AF_INET6: 655177633Sdfr a6 = (const struct sockaddr_in6 *) a; 656177633Sdfr b6 = (const struct sockaddr_in6 *) b; 657177633Sdfr return !memcmp(&a6->sin6_addr, &b6->sin6_addr, 658177633Sdfr sizeof(a6->sin6_addr)); 659177633Sdfr#endif 660177633Sdfr } 661177633Sdfr 662177633Sdfr return (0); 663177633Sdfr} 664177633Sdfr 665177633Sdfr/* 666177633Sdfr * Check for idle hosts and stop monitoring them. We could also free 667177633Sdfr * the host structure here, possibly after a larger timeout but that 668177633Sdfr * would require some care to avoid races with 669177633Sdfr * e.g. nlm_host_lock_count_sysctl. 670177633Sdfr */ 671177633Sdfrstatic void 672177633Sdfrnlm_check_idle(void) 673177633Sdfr{ 674177633Sdfr struct nlm_host *host; 675177633Sdfr 676177633Sdfr if (time_uptime <= nlm_next_idle_check) 677177633Sdfr return; 678177633Sdfr 679177633Sdfr nlm_next_idle_check = time_uptime + NLM_IDLE_PERIOD; 680177633Sdfr 681177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 682177633Sdfr if (host->nh_monitored 683177633Sdfr && time_uptime > host->nh_idle_timeout) { 684177633Sdfr if (lf_countlocks(host->nh_sysid) > 0) { 685177633Sdfr host->nh_idle_timeout = 686177633Sdfr time_uptime + NLM_IDLE_TIMEOUT; 687177633Sdfr continue; 688177633Sdfr } 689177633Sdfr nlm_host_unmonitor(host); 690177633Sdfr } 691177633Sdfr } 692177633Sdfr} 693177633Sdfr 694177633Sdfr/* 695177633Sdfr * Search for an existing NLM host that matches the given name 696177633Sdfr * (typically the caller_name element of an nlm4_lock). If none is 697177633Sdfr * found, create a new host. If 'rqstp' is non-NULL, record the remote 698177633Sdfr * address of the host so that we can call it back for async 699177633Sdfr * responses. 700177633Sdfr */ 701177633Sdfrstruct nlm_host * 702177633Sdfrnlm_find_host_by_name(const char *name, struct svc_req *rqstp) 703177633Sdfr{ 704177633Sdfr struct nlm_host *host; 705177633Sdfr 706177633Sdfr nlm_check_idle(); 707177633Sdfr 708177633Sdfr /* 709177633Sdfr * The remote host is determined by caller_name. 710177633Sdfr */ 711177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 712177633Sdfr if (!strcmp(host->nh_caller_name, name)) 713177633Sdfr break; 714177633Sdfr } 715177633Sdfr 716177633Sdfr if (!host) 717177633Sdfr host = nlm_create_host(name); 718177633Sdfr host->nh_idle_timeout = time_uptime + NLM_IDLE_TIMEOUT; 719177633Sdfr 720177633Sdfr /* 721177633Sdfr * If we have an RPC request, record the remote address so 722177633Sdfr * that can send async replies etc. 723177633Sdfr */ 724177633Sdfr if (rqstp) { 725177633Sdfr struct netbuf *addr = &rqstp->rq_xprt->xp_rtaddr; 726177633Sdfr 727177633Sdfr KASSERT(addr->len < sizeof(struct sockaddr_storage), 728177633Sdfr ("Strange remote transport address length")); 729177633Sdfr 730177633Sdfr /* 731177633Sdfr * If we have seen an address before and we currently 732177633Sdfr * have an RPC client handle, make sure the address is 733177633Sdfr * the same, otherwise discard the client handle. 734177633Sdfr */ 735177633Sdfr if (host->nh_addr.ss_len && host->nh_rpc) { 736177633Sdfr if (!nlm_compare_addr( 737177633Sdfr (struct sockaddr *) &host->nh_addr, 738177633Sdfr (struct sockaddr *) addr->buf) 739177633Sdfr || host->nh_vers != rqstp->rq_vers) { 740177633Sdfr AUTH_DESTROY(host->nh_rpc->cl_auth); 741177633Sdfr CLNT_DESTROY(host->nh_rpc); 742177633Sdfr host->nh_rpc = NULL; 743177633Sdfr } 744177633Sdfr } 745177633Sdfr memcpy(&host->nh_addr, addr->buf, addr->len); 746177633Sdfr host->nh_vers = rqstp->rq_vers; 747177633Sdfr } 748177633Sdfr 749177633Sdfr return (host); 750177633Sdfr} 751177633Sdfr 752177633Sdfr/* 753177633Sdfr * Search for an existing NLM host that matches the given remote 754177633Sdfr * address. If none is found, create a new host with the requested 755177633Sdfr * address and remember 'vers' as the NLM protocol version to use for 756177633Sdfr * that host. 757177633Sdfr */ 758177633Sdfrstruct nlm_host * 759177633Sdfrnlm_find_host_by_addr(const struct sockaddr *addr, int vers) 760177633Sdfr{ 761177633Sdfr struct nlm_host *host; 762177633Sdfr 763177633Sdfr nlm_check_idle(); 764177633Sdfr 765177633Sdfr /* 766177633Sdfr * The remote host is determined by caller_name. 767177633Sdfr */ 768177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 769177633Sdfr if (nlm_compare_addr(addr, 770177633Sdfr (const struct sockaddr *) &host->nh_addr)) 771177633Sdfr break; 772177633Sdfr } 773177633Sdfr 774177633Sdfr if (!host) { 775177633Sdfr /* 776177633Sdfr * Fake up a name using inet_ntop. This buffer is 777177633Sdfr * large enough for an IPv6 address. 778177633Sdfr */ 779177633Sdfr char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; 780177633Sdfr switch (addr->sa_family) { 781177633Sdfr case AF_INET: 782177633Sdfr __rpc_inet_ntop(AF_INET, 783177633Sdfr &((const struct sockaddr_in *) addr)->sin_addr, 784177633Sdfr tmp, sizeof tmp); 785177633Sdfr break; 786177633Sdfr#ifdef INET6 787177633Sdfr case AF_INET6: 788177633Sdfr __rpc_inet_ntop(AF_INET6, 789177633Sdfr &((const struct sockaddr_in6 *) addr)->sin6_addr, 790177633Sdfr tmp, sizeof tmp); 791177633Sdfr break; 792177633Sdfr#endif 793177633Sdfr default: 794177633Sdfr strcmp(tmp, "<unknown>"); 795177633Sdfr } 796177633Sdfr host = nlm_create_host(tmp); 797177633Sdfr memcpy(&host->nh_addr, addr, addr->sa_len); 798177633Sdfr host->nh_vers = vers; 799177633Sdfr } 800177633Sdfr host->nh_idle_timeout = time_uptime + NLM_IDLE_TIMEOUT; 801177633Sdfr 802177633Sdfr return (host); 803177633Sdfr} 804177633Sdfr 805177633Sdfr/* 806177633Sdfr * Find the NLM host that matches the value of 'sysid'. If none 807177633Sdfr * exists, return NULL. 808177633Sdfr */ 809177633Sdfrstatic struct nlm_host * 810177633Sdfrnlm_find_host_by_sysid(int sysid) 811177633Sdfr{ 812177633Sdfr struct nlm_host *host; 813177633Sdfr 814177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 815177633Sdfr if (host->nh_sysid == sysid) 816177633Sdfr return (host); 817177633Sdfr } 818177633Sdfr 819177633Sdfr return (NULL); 820177633Sdfr} 821177633Sdfr 822177633Sdfr/* 823177633Sdfr * Unregister this NLM host with the local NSM due to idleness. 824177633Sdfr */ 825177633Sdfrstatic void 826177633Sdfrnlm_host_unmonitor(struct nlm_host *host) 827177633Sdfr{ 828177633Sdfr mon_id smmonid; 829177633Sdfr sm_stat_res smstat; 830177633Sdfr struct timeval timo; 831177633Sdfr enum clnt_stat stat; 832177633Sdfr 833177633Sdfr if (nlm_debug_level >= 1) 834177633Sdfr printf("NLM: unmonitoring %s (sysid %d)\n", 835177633Sdfr host->nh_caller_name, host->nh_sysid); 836177633Sdfr 837177633Sdfr /* 838177633Sdfr * We put our assigned system ID value in the priv field to 839177633Sdfr * make it simpler to find the host if we are notified of a 840177633Sdfr * host restart. 841177633Sdfr */ 842177633Sdfr smmonid.mon_name = host->nh_caller_name; 843177633Sdfr smmonid.my_id.my_name = "localhost"; 844177633Sdfr smmonid.my_id.my_prog = NLM_PROG; 845177633Sdfr smmonid.my_id.my_vers = NLM_SM; 846177633Sdfr smmonid.my_id.my_proc = NLM_SM_NOTIFY; 847177633Sdfr 848177633Sdfr timo.tv_sec = 25; 849177633Sdfr timo.tv_usec = 0; 850177633Sdfr stat = CLNT_CALL(nlm_nsm, SM_UNMON, 851177633Sdfr (xdrproc_t) xdr_mon, &smmonid, 852177633Sdfr (xdrproc_t) xdr_sm_stat, &smstat, timo); 853177633Sdfr 854177633Sdfr if (stat != RPC_SUCCESS) { 855177633Sdfr printf("Failed to contact local NSM - rpc error %d\n", stat); 856177633Sdfr return; 857177633Sdfr } 858177633Sdfr if (smstat.res_stat == stat_fail) { 859177633Sdfr printf("Local NSM refuses to unmonitor %s\n", 860177633Sdfr host->nh_caller_name); 861177633Sdfr return; 862177633Sdfr } 863177633Sdfr 864177633Sdfr host->nh_monitored = FALSE; 865177633Sdfr} 866177633Sdfr 867177633Sdfr/* 868177633Sdfr * Register this NLM host with the local NSM so that we can be 869177633Sdfr * notified if it reboots. 870177633Sdfr */ 871177633Sdfrstatic void 872177633Sdfrnlm_host_monitor(struct nlm_host *host, int state) 873177633Sdfr{ 874177633Sdfr mon smmon; 875177633Sdfr sm_stat_res smstat; 876177633Sdfr struct timeval timo; 877177633Sdfr enum clnt_stat stat; 878177633Sdfr 879177633Sdfr if (host->nh_state && state && host->nh_state != state) { 880177633Sdfr /* 881177633Sdfr * The host rebooted without telling us. Trash its 882177633Sdfr * locks. 883177633Sdfr */ 884177633Sdfr nlm_host_notify(host, state, FALSE); 885177633Sdfr } 886177633Sdfr 887177633Sdfr if (state && !host->nh_state) { 888177633Sdfr /* 889177633Sdfr * This is the first time we have seen an NSM state 890177633Sdfr * value for this host. We record it here to help 891177633Sdfr * detect host reboots. 892177633Sdfr */ 893177633Sdfr host->nh_state = state; 894177633Sdfr if (nlm_debug_level >= 1) 895177633Sdfr printf("NLM: host %s (sysid %d) has NSM state %d\n", 896177633Sdfr host->nh_caller_name, host->nh_sysid, state); 897177633Sdfr } 898177633Sdfr 899177633Sdfr if (host->nh_monitored) 900177633Sdfr return; 901177633Sdfr 902177633Sdfr if (nlm_debug_level >= 1) 903177633Sdfr printf("NLM: monitoring %s (sysid %d)\n", 904177633Sdfr host->nh_caller_name, host->nh_sysid); 905177633Sdfr 906177633Sdfr /* 907177633Sdfr * We put our assigned system ID value in the priv field to 908177633Sdfr * make it simpler to find the host if we are notified of a 909177633Sdfr * host restart. 910177633Sdfr */ 911177633Sdfr smmon.mon_id.mon_name = host->nh_caller_name; 912177633Sdfr smmon.mon_id.my_id.my_name = "localhost"; 913177633Sdfr smmon.mon_id.my_id.my_prog = NLM_PROG; 914177633Sdfr smmon.mon_id.my_id.my_vers = NLM_SM; 915177633Sdfr smmon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; 916177633Sdfr memcpy(smmon.priv, &host->nh_sysid, sizeof(host->nh_sysid)); 917177633Sdfr 918177633Sdfr timo.tv_sec = 25; 919177633Sdfr timo.tv_usec = 0; 920177633Sdfr stat = CLNT_CALL(nlm_nsm, SM_MON, 921177633Sdfr (xdrproc_t) xdr_mon, &smmon, 922177633Sdfr (xdrproc_t) xdr_sm_stat, &smstat, timo); 923177633Sdfr 924177633Sdfr if (stat != RPC_SUCCESS) { 925177633Sdfr printf("Failed to contact local NSM - rpc error %d\n", stat); 926177633Sdfr return; 927177633Sdfr } 928177633Sdfr if (smstat.res_stat == stat_fail) { 929177633Sdfr printf("Local NSM refuses to monitor %s\n", 930177633Sdfr host->nh_caller_name); 931177633Sdfr return; 932177633Sdfr } 933177633Sdfr 934177633Sdfr host->nh_monitored = TRUE; 935177633Sdfr} 936177633Sdfr 937177633Sdfr/* 938177633Sdfr * Return an RPC client handle that can be used to talk to the NLM 939177633Sdfr * running on the given host. 940177633Sdfr */ 941177633SdfrCLIENT * 942177633Sdfrnlm_host_get_rpc(struct nlm_host *host) 943177633Sdfr{ 944177633Sdfr struct timeval zero; 945177633Sdfr 946177633Sdfr if (host->nh_rpc) 947177633Sdfr return (host->nh_rpc); 948177633Sdfr 949177633Sdfr /* 950177633Sdfr * Set the send timeout to zero - we only use this rpc handle 951177633Sdfr * for sending async replies which have no return value. 952177633Sdfr */ 953177633Sdfr host->nh_rpc = nlm_get_rpc((struct sockaddr *)&host->nh_addr, 954177633Sdfr NLM_PROG, host->nh_vers); 955177633Sdfr 956177633Sdfr if (host->nh_rpc) { 957177633Sdfr zero.tv_sec = 0; 958177633Sdfr zero.tv_usec = 0; 959177633Sdfr CLNT_CONTROL(host->nh_rpc, CLSET_TIMEOUT, &zero); 960177633Sdfr 961177633Sdfr /* 962177633Sdfr * Monitor the host - if it reboots, the address of 963177633Sdfr * its NSM might change so we must discard our RPC 964177633Sdfr * handle. 965177633Sdfr */ 966177633Sdfr nlm_host_monitor(host, 0); 967177633Sdfr } 968177633Sdfr 969177633Sdfr return (host->nh_rpc); 970177633Sdfr} 971177633Sdfr 972177633Sdfr/**********************************************************************/ 973177633Sdfr 974177633Sdfr/* 975177633Sdfr * Syscall interface with userland. 976177633Sdfr */ 977177633Sdfr 978177633Sdfrextern void nlm_prog_0(struct svc_req *rqstp, SVCXPRT *transp); 979177633Sdfrextern void nlm_prog_1(struct svc_req *rqstp, SVCXPRT *transp); 980177633Sdfrextern void nlm_prog_3(struct svc_req *rqstp, SVCXPRT *transp); 981177633Sdfrextern void nlm_prog_4(struct svc_req *rqstp, SVCXPRT *transp); 982177633Sdfr 983177633Sdfrstatic int 984177633Sdfrnlm_register_services(SVCPOOL *pool, int addr_count, char **addrs) 985177633Sdfr{ 986177633Sdfr static rpcvers_t versions[] = { 987177633Sdfr NLM_SM, NLM_VERS, NLM_VERSX, NLM_VERS4 988177633Sdfr }; 989177633Sdfr static void (*dispatchers[])(struct svc_req *, SVCXPRT *) = { 990177633Sdfr nlm_prog_0, nlm_prog_1, nlm_prog_3, nlm_prog_4 991177633Sdfr }; 992177633Sdfr static const int version_count = sizeof(versions) / sizeof(versions[0]); 993177633Sdfr 994177633Sdfr SVCXPRT **xprts; 995177633Sdfr char netid[16]; 996177633Sdfr char uaddr[128]; 997177633Sdfr struct netconfig *nconf; 998177633Sdfr int i, j, error; 999177633Sdfr 1000177633Sdfr if (!addr_count) { 1001177633Sdfr printf("NLM: no service addresses given - can't start server"); 1002177633Sdfr return (EINVAL); 1003177633Sdfr } 1004177633Sdfr 1005177633Sdfr xprts = malloc(addr_count * sizeof(SVCXPRT *), M_NLM, M_WAITOK); 1006177633Sdfr for (i = 0; i < version_count; i++) { 1007177633Sdfr for (j = 0; j < addr_count; j++) { 1008177633Sdfr /* 1009177633Sdfr * Create transports for the first version and 1010177633Sdfr * then just register everything else to the 1011177633Sdfr * same transports. 1012177633Sdfr */ 1013177633Sdfr if (i == 0) { 1014177633Sdfr char *up; 1015177633Sdfr 1016177633Sdfr error = copyin(&addrs[2*j], &up, 1017177633Sdfr sizeof(char*)); 1018177633Sdfr if (error) 1019177633Sdfr goto out; 1020177633Sdfr error = copyinstr(up, netid, sizeof(netid), 1021177633Sdfr NULL); 1022177633Sdfr if (error) 1023177633Sdfr goto out; 1024177633Sdfr error = copyin(&addrs[2*j+1], &up, 1025177633Sdfr sizeof(char*)); 1026177633Sdfr if (error) 1027177633Sdfr goto out; 1028177633Sdfr error = copyinstr(up, uaddr, sizeof(uaddr), 1029177633Sdfr NULL); 1030177633Sdfr if (error) 1031177633Sdfr goto out; 1032177633Sdfr nconf = getnetconfigent(netid); 1033177633Sdfr if (!nconf) { 1034177633Sdfr printf("Can't lookup netid %s\n", 1035177633Sdfr netid); 1036177633Sdfr error = EINVAL; 1037177633Sdfr goto out; 1038177633Sdfr } 1039177633Sdfr xprts[j] = svc_tp_create(pool, dispatchers[i], 1040177633Sdfr NLM_PROG, versions[i], uaddr, nconf); 1041177633Sdfr if (!xprts[j]) { 1042177633Sdfr printf("NLM: unable to create " 1043177633Sdfr "(NLM_PROG, %d).\n", versions[i]); 1044177633Sdfr error = EINVAL; 1045177633Sdfr goto out; 1046177633Sdfr } 1047177633Sdfr freenetconfigent(nconf); 1048177633Sdfr } else { 1049177633Sdfr nconf = getnetconfigent(xprts[j]->xp_netid); 1050177633Sdfr rpcb_unset(NLM_PROG, versions[i], nconf); 1051177633Sdfr if (!svc_reg(xprts[j], NLM_PROG, versions[i], 1052177633Sdfr dispatchers[i], nconf)) { 1053177633Sdfr printf("NLM: can't register " 1054177633Sdfr "(NLM_PROG, %d)\n", versions[i]); 1055177633Sdfr error = EINVAL; 1056177633Sdfr goto out; 1057177633Sdfr } 1058177633Sdfr } 1059177633Sdfr } 1060177633Sdfr } 1061177633Sdfr error = 0; 1062177633Sdfrout: 1063177633Sdfr free(xprts, M_NLM); 1064177633Sdfr return (error); 1065177633Sdfr} 1066177633Sdfr 1067177633Sdfr/* 1068177633Sdfr * Main server entry point. Contacts the local NSM to get its current 1069177633Sdfr * state and send SM_UNMON_ALL. Registers the NLM services and then 1070177633Sdfr * services requests. Does not return until the server is interrupted 1071177633Sdfr * by a signal. 1072177633Sdfr */ 1073177633Sdfrstatic int 1074177633Sdfrnlm_server_main(int addr_count, char **addrs) 1075177633Sdfr{ 1076177633Sdfr struct thread *td = curthread; 1077177633Sdfr int error; 1078177633Sdfr SVCPOOL *pool; 1079177633Sdfr struct sockopt opt; 1080177633Sdfr int portlow; 1081177633Sdfr#ifdef INET6 1082177633Sdfr struct sockaddr_in6 sin6; 1083177633Sdfr#endif 1084177633Sdfr struct sockaddr_in sin; 1085177633Sdfr my_id id; 1086177633Sdfr sm_stat smstat; 1087177633Sdfr struct timeval timo; 1088177633Sdfr enum clnt_stat stat; 1089177633Sdfr struct nlm_host *host; 1090177633Sdfr 1091177633Sdfr if (nlm_socket) { 1092177633Sdfr printf("NLM: can't start server - it appears to be running already\n"); 1093177633Sdfr return (EPERM); 1094177633Sdfr } 1095177633Sdfr 1096177633Sdfr memset(&opt, 0, sizeof(opt)); 1097177633Sdfr 1098177633Sdfr nlm_socket = NULL; 1099177633Sdfr error = socreate(AF_INET, &nlm_socket, SOCK_DGRAM, 0, 1100177633Sdfr td->td_ucred, td); 1101177633Sdfr if (error) { 1102177633Sdfr printf("NLM: can't create IPv4 socket - error %d\n", error); 1103177633Sdfr return (error); 1104177633Sdfr } 1105177633Sdfr opt.sopt_dir = SOPT_SET; 1106177633Sdfr opt.sopt_level = IPPROTO_IP; 1107177633Sdfr opt.sopt_name = IP_PORTRANGE; 1108177633Sdfr portlow = IP_PORTRANGE_LOW; 1109177633Sdfr opt.sopt_val = &portlow; 1110177633Sdfr opt.sopt_valsize = sizeof(portlow); 1111177633Sdfr sosetopt(nlm_socket, &opt); 1112177633Sdfr 1113177633Sdfr#ifdef INET6 1114177633Sdfr nlm_socket6 = NULL; 1115177633Sdfr error = socreate(AF_INET6, &nlm_socket6, SOCK_DGRAM, 0, 1116177633Sdfr td->td_ucred, td); 1117177633Sdfr if (error) { 1118177633Sdfr printf("NLM: can't create IPv6 socket - error %d\n", error); 1119177633Sdfr return (error); 1120177633Sdfr } 1121177633Sdfr opt.sopt_dir = SOPT_SET; 1122177633Sdfr opt.sopt_level = IPPROTO_IPV6; 1123177633Sdfr opt.sopt_name = IPV6_PORTRANGE; 1124177633Sdfr portlow = IPV6_PORTRANGE_LOW; 1125177633Sdfr opt.sopt_val = &portlow; 1126177633Sdfr opt.sopt_valsize = sizeof(portlow); 1127177633Sdfr sosetopt(nlm_socket6, &opt); 1128177633Sdfr#endif 1129177633Sdfr 1130177633Sdfr#ifdef INET6 1131177633Sdfr memset(&sin6, 0, sizeof(sin6)); 1132177633Sdfr sin6.sin6_len = sizeof(sin6); 1133177633Sdfr sin6.sin6_family = AF_INET6; 1134177633Sdfr sin6.sin6_addr = in6addr_loopback; 1135177633Sdfr nlm_nsm = nlm_get_rpc((struct sockaddr *) &sin6, SM_PROG, SM_VERS); 1136177633Sdfr if (!nlm_nsm) { 1137177633Sdfr#endif 1138177633Sdfr memset(&sin, 0, sizeof(sin)); 1139177633Sdfr sin.sin_len = sizeof(sin); 1140177633Sdfr sin.sin_family = AF_INET6; 1141177633Sdfr sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1142177633Sdfr nlm_nsm = nlm_get_rpc((struct sockaddr *) &sin, SM_PROG, 1143177633Sdfr SM_VERS); 1144177633Sdfr#ifdef INET6 1145177633Sdfr } 1146177633Sdfr#endif 1147177633Sdfr 1148177633Sdfr if (!nlm_nsm) { 1149177633Sdfr printf("Can't start NLM - unable to contact NSM\n"); 1150177633Sdfr return (EINVAL); 1151177633Sdfr } 1152177633Sdfr 1153177633Sdfr pool = svcpool_create(); 1154177633Sdfr 1155177633Sdfr error = nlm_register_services(pool, addr_count, addrs); 1156177633Sdfr if (error) 1157177633Sdfr goto out; 1158177633Sdfr 1159177633Sdfr memset(&id, 0, sizeof(id)); 1160177633Sdfr id.my_name = "NFS NLM"; 1161177633Sdfr 1162177633Sdfr timo.tv_sec = 25; 1163177633Sdfr timo.tv_usec = 0; 1164177633Sdfr stat = CLNT_CALL(nlm_nsm, SM_UNMON_ALL, 1165177633Sdfr (xdrproc_t) xdr_my_id, &id, 1166177633Sdfr (xdrproc_t) xdr_sm_stat, &smstat, timo); 1167177633Sdfr 1168177633Sdfr if (stat != RPC_SUCCESS) { 1169177633Sdfr struct rpc_err err; 1170177633Sdfr 1171177633Sdfr CLNT_GETERR(nlm_nsm, &err); 1172177633Sdfr printf("NLM: unexpected error contacting NSM, stat=%d, errno=%d\n", 1173177633Sdfr stat, err.re_errno); 1174177633Sdfr error = EINVAL; 1175177633Sdfr goto out; 1176177633Sdfr } 1177177633Sdfr 1178177633Sdfr if (nlm_debug_level >= 1) 1179177633Sdfr printf("NLM: local NSM state is %d\n", smstat.state); 1180177633Sdfr 1181177633Sdfr svc_run(pool); 1182177633Sdfr error = 0; 1183177633Sdfr 1184177633Sdfrout: 1185177633Sdfr if (pool) 1186177633Sdfr svcpool_destroy(pool); 1187177633Sdfr 1188177633Sdfr /* 1189177633Sdfr * Trash all the existing state so that if the server 1190177633Sdfr * restarts, it gets a clean slate. 1191177633Sdfr */ 1192177633Sdfr while ((host = TAILQ_FIRST(&nlm_hosts)) != NULL) { 1193177633Sdfr nlm_host_notify(host, 0, TRUE); 1194177633Sdfr } 1195177633Sdfr if (nlm_nsm) { 1196177633Sdfr AUTH_DESTROY(nlm_nsm->cl_auth); 1197177633Sdfr CLNT_DESTROY(nlm_nsm); 1198177633Sdfr nlm_nsm = NULL; 1199177633Sdfr } 1200177633Sdfr if (nlm_lockd) { 1201177633Sdfr AUTH_DESTROY(nlm_lockd->cl_auth); 1202177633Sdfr CLNT_DESTROY(nlm_lockd); 1203177633Sdfr nlm_lockd = NULL; 1204177633Sdfr } 1205177633Sdfr 1206177633Sdfr soclose(nlm_socket); 1207177633Sdfr nlm_socket = NULL; 1208177633Sdfr#ifdef INET6 1209177633Sdfr soclose(nlm_socket6); 1210177633Sdfr nlm_socket6 = NULL; 1211177633Sdfr#endif 1212177633Sdfr 1213177633Sdfr return (error); 1214177633Sdfr} 1215177633Sdfr 1216177633Sdfrint 1217177633Sdfrnlm_syscall(struct thread *td, struct nlm_syscall_args *uap) 1218177633Sdfr{ 1219177633Sdfr int error; 1220177633Sdfr 1221177685Sdfr#if __FreeBSD_version >= 700000 1222177633Sdfr error = priv_check(td, PRIV_NFS_LOCKD); 1223177685Sdfr#else 1224177685Sdfr error = suser(td); 1225177685Sdfr#endif 1226177633Sdfr if (error) 1227177633Sdfr return (error); 1228177633Sdfr 1229177633Sdfr nlm_debug_level = uap->debug_level; 1230177633Sdfr nlm_grace_threshold = time_uptime + uap->grace_period; 1231177633Sdfr nlm_next_idle_check = time_uptime + NLM_IDLE_PERIOD; 1232177633Sdfr 1233177633Sdfr return nlm_server_main(uap->addr_count, uap->addrs); 1234177633Sdfr} 1235177633Sdfr 1236177633Sdfr/**********************************************************************/ 1237177633Sdfr 1238177633Sdfr/* 1239177633Sdfr * NLM implementation details, called from the RPC stubs. 1240177633Sdfr */ 1241177633Sdfr 1242177633Sdfr 1243177633Sdfrvoid 1244177633Sdfrnlm_sm_notify(struct nlm_sm_status *argp) 1245177633Sdfr{ 1246177633Sdfr uint32_t sysid; 1247177633Sdfr struct nlm_host *host; 1248177633Sdfr 1249177633Sdfr if (nlm_debug_level >= 3) 1250177633Sdfr printf("nlm_sm_notify(): mon_name = %s\n", argp->mon_name); 1251177633Sdfr memcpy(&sysid, &argp->priv, sizeof(sysid)); 1252177633Sdfr host = nlm_find_host_by_sysid(sysid); 1253177633Sdfr if (host) 1254177633Sdfr nlm_host_notify(host, argp->state, FALSE); 1255177633Sdfr} 1256177633Sdfr 1257177633Sdfrstatic void 1258177633Sdfrnlm_convert_to_fhandle_t(fhandle_t *fhp, struct netobj *p) 1259177633Sdfr{ 1260177633Sdfr memcpy(fhp, p->n_bytes, sizeof(fhandle_t)); 1261177633Sdfr} 1262177633Sdfr 1263177633Sdfrstruct vfs_state { 1264177633Sdfr struct mount *vs_mp; 1265177633Sdfr struct vnode *vs_vp; 1266177633Sdfr int vs_vfslocked; 1267177633Sdfr}; 1268177633Sdfr 1269177633Sdfrstatic int 1270177633Sdfrnlm_get_vfs_state(struct nlm_host *host, struct svc_req *rqstp, 1271177633Sdfr fhandle_t *fhp, struct vfs_state *vs) 1272177633Sdfr{ 1273177633Sdfr int error, exflags, freecred; 1274177633Sdfr struct ucred *cred = NULL, *credanon; 1275177633Sdfr 1276177633Sdfr memset(vs, 0, sizeof(*vs)); 1277177633Sdfr freecred = FALSE; 1278177633Sdfr 1279177633Sdfr vs->vs_mp = vfs_getvfs(&fhp->fh_fsid); 1280177633Sdfr if (!vs->vs_mp) { 1281177633Sdfr return (ESTALE); 1282177633Sdfr } 1283177633Sdfr vs->vs_vfslocked = VFS_LOCK_GIANT(vs->vs_mp); 1284177633Sdfr 1285177633Sdfr error = VFS_CHECKEXP(vs->vs_mp, (struct sockaddr *)&host->nh_addr, 1286177633Sdfr &exflags, &credanon); 1287177633Sdfr if (error) 1288177633Sdfr goto out; 1289177633Sdfr 1290177633Sdfr if (exflags & MNT_EXRDONLY || (vs->vs_mp->mnt_flag & MNT_RDONLY)) { 1291177633Sdfr error = EROFS; 1292177633Sdfr goto out; 1293177633Sdfr } 1294177633Sdfr 1295177633Sdfr error = VFS_FHTOVP(vs->vs_mp, &fhp->fh_fid, &vs->vs_vp); 1296177633Sdfr if (error) 1297177633Sdfr goto out; 1298177633Sdfr 1299177633Sdfr cred = crget(); 1300177633Sdfr freecred = TRUE; 1301177633Sdfr if (!svc_getcred(rqstp, cred, NULL)) { 1302177633Sdfr error = EINVAL; 1303177633Sdfr goto out; 1304177633Sdfr } 1305177633Sdfr if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) { 1306177633Sdfr crfree(cred); 1307177633Sdfr cred = credanon; 1308177633Sdfr freecred = FALSE; 1309177633Sdfr } 1310177633Sdfr#if __FreeBSD_version < 800011 1311177633Sdfr VOP_UNLOCK(vs->vs_vp, 0, curthread); 1312177633Sdfr#else 1313177633Sdfr VOP_UNLOCK(vs->vs_vp, 0); 1314177633Sdfr#endif 1315177633Sdfr 1316177633Sdfr /* 1317177633Sdfr * Check cred. 1318177633Sdfr */ 1319177633Sdfr error = VOP_ACCESS(vs->vs_vp, VWRITE, cred, curthread); 1320177633Sdfr if (error) 1321177633Sdfr goto out; 1322177633Sdfr 1323177633Sdfrout: 1324177633Sdfr if (freecred) 1325177633Sdfr crfree(cred); 1326177633Sdfr 1327177633Sdfr return (error); 1328177633Sdfr} 1329177633Sdfr 1330177633Sdfrstatic void 1331177633Sdfrnlm_release_vfs_state(struct vfs_state *vs) 1332177633Sdfr{ 1333177633Sdfr 1334177633Sdfr if (vs->vs_vp) 1335177633Sdfr vrele(vs->vs_vp); 1336177633Sdfr if (vs->vs_mp) 1337177633Sdfr vfs_rel(vs->vs_mp); 1338177633Sdfr VFS_UNLOCK_GIANT(vs->vs_vfslocked); 1339177633Sdfr} 1340177633Sdfr 1341177633Sdfrstatic nlm4_stats 1342177633Sdfrnlm_convert_error(int error) 1343177633Sdfr{ 1344177633Sdfr 1345177633Sdfr if (error == ESTALE) 1346177633Sdfr return nlm4_stale_fh; 1347177633Sdfr else if (error == EROFS) 1348177633Sdfr return nlm4_rofs; 1349177633Sdfr else 1350177633Sdfr return nlm4_failed; 1351177633Sdfr} 1352177633Sdfr 1353177633Sdfrstruct nlm_host * 1354177633Sdfrnlm_do_test(nlm4_testargs *argp, nlm4_testres *result, struct svc_req *rqstp) 1355177633Sdfr{ 1356177633Sdfr fhandle_t fh; 1357177633Sdfr struct vfs_state vs; 1358177633Sdfr struct nlm_host *host, *bhost; 1359177633Sdfr int error, sysid; 1360177633Sdfr struct flock fl; 1361177633Sdfr 1362177633Sdfr memset(result, 0, sizeof(*result)); 1363177633Sdfr 1364177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1365177633Sdfr if (!host) { 1366177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1367177633Sdfr return (NULL); 1368177633Sdfr } 1369177633Sdfr 1370177633Sdfr if (nlm_debug_level >= 3) 1371177633Sdfr printf("nlm_do_test(): caller_name = %s (sysid = %d)\n", 1372177633Sdfr host->nh_caller_name, host->nh_sysid); 1373177633Sdfr 1374177633Sdfr nlm_free_finished_locks(host); 1375177633Sdfr sysid = host->nh_sysid; 1376177633Sdfr 1377177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1378177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1379177633Sdfr 1380177633Sdfr if (time_uptime < nlm_grace_threshold) { 1381177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1382177633Sdfr return (host); 1383177633Sdfr } 1384177633Sdfr 1385177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1386177633Sdfr if (error) { 1387177633Sdfr result->stat.stat = nlm_convert_error(error); 1388177633Sdfr goto out; 1389177633Sdfr } 1390177633Sdfr 1391177633Sdfr fl.l_start = argp->alock.l_offset; 1392177633Sdfr fl.l_len = argp->alock.l_len; 1393177633Sdfr fl.l_pid = argp->alock.svid; 1394177633Sdfr fl.l_sysid = sysid; 1395177633Sdfr fl.l_whence = SEEK_SET; 1396177633Sdfr if (argp->exclusive) 1397177633Sdfr fl.l_type = F_WRLCK; 1398177633Sdfr else 1399177633Sdfr fl.l_type = F_RDLCK; 1400177633Sdfr error = VOP_ADVLOCK(vs.vs_vp, NULL, F_GETLK, &fl, F_REMOTE); 1401177633Sdfr if (error) { 1402177633Sdfr result->stat.stat = nlm4_failed; 1403177633Sdfr goto out; 1404177633Sdfr } 1405177633Sdfr 1406177633Sdfr if (fl.l_type == F_UNLCK) { 1407177633Sdfr result->stat.stat = nlm4_granted; 1408177633Sdfr } else { 1409177633Sdfr result->stat.stat = nlm4_denied; 1410177633Sdfr result->stat.nlm4_testrply_u.holder.exclusive = 1411177633Sdfr (fl.l_type == F_WRLCK); 1412177633Sdfr result->stat.nlm4_testrply_u.holder.svid = fl.l_pid; 1413177633Sdfr bhost = nlm_find_host_by_sysid(fl.l_sysid); 1414177633Sdfr if (bhost) { 1415177633Sdfr /* 1416177633Sdfr * We don't have any useful way of recording 1417177633Sdfr * the value of oh used in the original lock 1418177633Sdfr * request. Ideally, the test reply would have 1419177633Sdfr * a space for the owning host's name allowing 1420177633Sdfr * our caller's NLM to keep track. 1421177633Sdfr * 1422177633Sdfr * As far as I can see, Solaris uses an eight 1423177633Sdfr * byte structure for oh which contains a four 1424177633Sdfr * byte pid encoded in local byte order and 1425177633Sdfr * the first four bytes of the host 1426177633Sdfr * name. Linux uses a variable length string 1427177633Sdfr * 'pid@hostname' in ascii but doesn't even 1428177633Sdfr * return that in test replies. 1429177633Sdfr * 1430177633Sdfr * For the moment, return nothing in oh 1431177633Sdfr * (already zero'ed above). 1432177633Sdfr */ 1433177633Sdfr } 1434177633Sdfr result->stat.nlm4_testrply_u.holder.l_offset = fl.l_start; 1435177633Sdfr result->stat.nlm4_testrply_u.holder.l_len = fl.l_len; 1436177633Sdfr } 1437177633Sdfr 1438177633Sdfrout: 1439177633Sdfr nlm_release_vfs_state(&vs); 1440177633Sdfr return (host); 1441177633Sdfr} 1442177633Sdfr 1443177633Sdfrstruct nlm_host * 1444177633Sdfrnlm_do_lock(nlm4_lockargs *argp, nlm4_res *result, struct svc_req *rqstp, 1445177633Sdfr bool_t monitor) 1446177633Sdfr{ 1447177633Sdfr fhandle_t fh; 1448177633Sdfr struct vfs_state vs; 1449177633Sdfr struct nlm_host *host; 1450177633Sdfr int error, sysid; 1451177633Sdfr struct flock fl; 1452177633Sdfr 1453177633Sdfr memset(result, 0, sizeof(*result)); 1454177633Sdfr 1455177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1456177633Sdfr if (!host) { 1457177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1458177633Sdfr return (NULL); 1459177633Sdfr } 1460177633Sdfr 1461177633Sdfr if (nlm_debug_level >= 3) 1462177633Sdfr printf("nlm_do_lock(): caller_name = %s (sysid = %d)\n", 1463177633Sdfr host->nh_caller_name, host->nh_sysid); 1464177633Sdfr 1465177633Sdfr nlm_free_finished_locks(host); 1466177633Sdfr sysid = host->nh_sysid; 1467177633Sdfr 1468177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1469177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1470177633Sdfr 1471177633Sdfr if (time_uptime < nlm_grace_threshold && !argp->reclaim) { 1472177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1473177633Sdfr return (host); 1474177633Sdfr } 1475177633Sdfr 1476177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1477177633Sdfr if (error) { 1478177633Sdfr result->stat.stat = nlm_convert_error(error); 1479177633Sdfr goto out; 1480177633Sdfr } 1481177633Sdfr 1482177633Sdfr fl.l_start = argp->alock.l_offset; 1483177633Sdfr fl.l_len = argp->alock.l_len; 1484177633Sdfr fl.l_pid = argp->alock.svid; 1485177633Sdfr fl.l_sysid = sysid; 1486177633Sdfr fl.l_whence = SEEK_SET; 1487177633Sdfr if (argp->exclusive) 1488177633Sdfr fl.l_type = F_WRLCK; 1489177633Sdfr else 1490177633Sdfr fl.l_type = F_RDLCK; 1491177633Sdfr if (argp->block) { 1492177633Sdfr struct nlm_async_lock *af; 1493177633Sdfr 1494177633Sdfr /* 1495177633Sdfr * First, make sure we can contact the host's NLM. 1496177633Sdfr */ 1497177633Sdfr if (!nlm_host_get_rpc(host)) { 1498177633Sdfr result->stat.stat = nlm4_failed; 1499177633Sdfr goto out; 1500177633Sdfr } 1501177633Sdfr 1502177633Sdfr /* 1503177633Sdfr * First we need to check and see if there is an 1504177633Sdfr * existing blocked lock that matches. This could be a 1505177633Sdfr * badly behaved client or an RPC re-send. If we find 1506177633Sdfr * one, just return nlm4_blocked. 1507177633Sdfr */ 1508177633Sdfr mtx_lock(&host->nh_lock); 1509177633Sdfr TAILQ_FOREACH(af, &host->nh_pending, af_link) { 1510177633Sdfr if (af->af_fl.l_start == fl.l_start 1511177633Sdfr && af->af_fl.l_len == fl.l_len 1512177633Sdfr && af->af_fl.l_pid == fl.l_pid 1513177633Sdfr && af->af_fl.l_type == fl.l_type) { 1514177633Sdfr break; 1515177633Sdfr } 1516177633Sdfr } 1517177633Sdfr mtx_unlock(&host->nh_lock); 1518177633Sdfr if (af) { 1519177633Sdfr result->stat.stat = nlm4_blocked; 1520177633Sdfr goto out; 1521177633Sdfr } 1522177633Sdfr 1523177633Sdfr af = malloc(sizeof(struct nlm_async_lock), M_NLM, 1524177633Sdfr M_WAITOK|M_ZERO); 1525177633Sdfr TASK_INIT(&af->af_task, 0, nlm_lock_callback, af); 1526177633Sdfr af->af_vp = vs.vs_vp; 1527177633Sdfr af->af_fl = fl; 1528177633Sdfr af->af_host = host; 1529177633Sdfr /* 1530177633Sdfr * We use M_RPC here so that we can xdr_free the thing 1531177633Sdfr * later. 1532177633Sdfr */ 1533177633Sdfr af->af_granted.exclusive = argp->exclusive; 1534177633Sdfr af->af_granted.alock.caller_name = 1535177633Sdfr strdup(argp->alock.caller_name, M_RPC); 1536177633Sdfr nlm_copy_netobj(&af->af_granted.alock.fh, 1537177633Sdfr &argp->alock.fh, M_RPC); 1538177633Sdfr nlm_copy_netobj(&af->af_granted.alock.oh, 1539177633Sdfr &argp->alock.oh, M_RPC); 1540177633Sdfr af->af_granted.alock.svid = argp->alock.svid; 1541177633Sdfr af->af_granted.alock.l_offset = argp->alock.l_offset; 1542177633Sdfr af->af_granted.alock.l_len = argp->alock.l_len; 1543177633Sdfr 1544177633Sdfr /* 1545177633Sdfr * Put the entry on the pending list before calling 1546177633Sdfr * VOP_ADVLOCKASYNC. We do this in case the lock 1547177633Sdfr * request was blocked (returning EINPROGRESS) but 1548177633Sdfr * then granted before we manage to run again. The 1549177633Sdfr * client may receive the granted message before we 1550177633Sdfr * send our blocked reply but thats their problem. 1551177633Sdfr */ 1552177633Sdfr mtx_lock(&host->nh_lock); 1553177633Sdfr TAILQ_INSERT_TAIL(&host->nh_pending, af, af_link); 1554177633Sdfr mtx_unlock(&host->nh_lock); 1555177633Sdfr 1556177633Sdfr error = VOP_ADVLOCKASYNC(vs.vs_vp, NULL, F_SETLK, &fl, F_REMOTE, 1557177633Sdfr &af->af_task, &af->af_cookie); 1558177633Sdfr 1559177633Sdfr /* 1560177633Sdfr * If the lock completed synchronously, just free the 1561177633Sdfr * tracking structure now. 1562177633Sdfr */ 1563177633Sdfr if (error != EINPROGRESS) { 1564177633Sdfr mtx_lock(&host->nh_lock); 1565177633Sdfr TAILQ_REMOVE(&host->nh_pending, af, af_link); 1566177633Sdfr mtx_unlock(&host->nh_lock); 1567177633Sdfr xdr_free((xdrproc_t) xdr_nlm4_testargs, 1568177633Sdfr &af->af_granted); 1569177633Sdfr free(af, M_NLM); 1570177633Sdfr } else { 1571177633Sdfr if (nlm_debug_level >= 2) 1572177633Sdfr printf("NLM: pending async lock %p for %s " 1573177633Sdfr "(sysid %d)\n", 1574177633Sdfr af, host->nh_caller_name, sysid); 1575177633Sdfr /* 1576177633Sdfr * Don't vrele the vnode just yet - this must 1577177633Sdfr * wait until either the async callback 1578177633Sdfr * happens or the lock is cancelled. 1579177633Sdfr */ 1580177633Sdfr vs.vs_vp = NULL; 1581177633Sdfr } 1582177633Sdfr } else { 1583177633Sdfr error = VOP_ADVLOCK(vs.vs_vp, NULL, F_SETLK, &fl, F_REMOTE); 1584177633Sdfr } 1585177633Sdfr 1586177633Sdfr if (error) { 1587177633Sdfr if (error == EINPROGRESS) { 1588177633Sdfr result->stat.stat = nlm4_blocked; 1589177633Sdfr } else if (error == EDEADLK) { 1590177633Sdfr result->stat.stat = nlm4_deadlck; 1591177633Sdfr } else if (error == EAGAIN) { 1592177633Sdfr result->stat.stat = nlm4_denied; 1593177633Sdfr } else { 1594177633Sdfr result->stat.stat = nlm4_failed; 1595177633Sdfr } 1596177633Sdfr } else { 1597177633Sdfr if (monitor) 1598177633Sdfr nlm_host_monitor(host, argp->state); 1599177633Sdfr result->stat.stat = nlm4_granted; 1600177633Sdfr } 1601177633Sdfr 1602177633Sdfrout: 1603177633Sdfr nlm_release_vfs_state(&vs); 1604177633Sdfr 1605177633Sdfr return (host); 1606177633Sdfr} 1607177633Sdfr 1608177633Sdfrstruct nlm_host * 1609177633Sdfrnlm_do_cancel(nlm4_cancargs *argp, nlm4_res *result, struct svc_req *rqstp) 1610177633Sdfr{ 1611177633Sdfr fhandle_t fh; 1612177633Sdfr struct vfs_state vs; 1613177633Sdfr struct nlm_host *host; 1614177633Sdfr int error, sysid; 1615177633Sdfr struct flock fl; 1616177633Sdfr struct nlm_async_lock *af; 1617177633Sdfr 1618177633Sdfr memset(result, 0, sizeof(*result)); 1619177633Sdfr 1620177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1621177633Sdfr if (!host) { 1622177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1623177633Sdfr return (NULL); 1624177633Sdfr } 1625177633Sdfr 1626177633Sdfr if (nlm_debug_level >= 3) 1627177633Sdfr printf("nlm_do_cancel(): caller_name = %s (sysid = %d)\n", 1628177633Sdfr host->nh_caller_name, host->nh_sysid); 1629177633Sdfr 1630177633Sdfr nlm_free_finished_locks(host); 1631177633Sdfr sysid = host->nh_sysid; 1632177633Sdfr 1633177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1634177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1635177633Sdfr 1636177633Sdfr if (time_uptime < nlm_grace_threshold) { 1637177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1638177633Sdfr return (host); 1639177633Sdfr } 1640177633Sdfr 1641177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1642177633Sdfr if (error) { 1643177633Sdfr result->stat.stat = nlm_convert_error(error); 1644177633Sdfr goto out; 1645177633Sdfr } 1646177633Sdfr 1647177633Sdfr fl.l_start = argp->alock.l_offset; 1648177633Sdfr fl.l_len = argp->alock.l_len; 1649177633Sdfr fl.l_pid = argp->alock.svid; 1650177633Sdfr fl.l_sysid = sysid; 1651177633Sdfr fl.l_whence = SEEK_SET; 1652177633Sdfr if (argp->exclusive) 1653177633Sdfr fl.l_type = F_WRLCK; 1654177633Sdfr else 1655177633Sdfr fl.l_type = F_RDLCK; 1656177633Sdfr 1657177633Sdfr /* 1658177633Sdfr * First we need to try and find the async lock request - if 1659177633Sdfr * there isn't one, we give up and return nlm4_denied. 1660177633Sdfr */ 1661177633Sdfr mtx_lock(&host->nh_lock); 1662177633Sdfr 1663177633Sdfr TAILQ_FOREACH(af, &host->nh_pending, af_link) { 1664177633Sdfr if (af->af_fl.l_start == fl.l_start 1665177633Sdfr && af->af_fl.l_len == fl.l_len 1666177633Sdfr && af->af_fl.l_pid == fl.l_pid 1667177633Sdfr && af->af_fl.l_type == fl.l_type) { 1668177633Sdfr break; 1669177633Sdfr } 1670177633Sdfr } 1671177633Sdfr 1672177633Sdfr if (!af) { 1673177633Sdfr mtx_unlock(&host->nh_lock); 1674177633Sdfr result->stat.stat = nlm4_denied; 1675177633Sdfr goto out; 1676177633Sdfr } 1677177633Sdfr 1678177633Sdfr error = nlm_cancel_async_lock(af); 1679177633Sdfr 1680177633Sdfr if (error) { 1681177633Sdfr result->stat.stat = nlm4_denied; 1682177633Sdfr } else { 1683177633Sdfr result->stat.stat = nlm4_granted; 1684177633Sdfr } 1685177633Sdfr 1686177633Sdfr mtx_unlock(&host->nh_lock); 1687177633Sdfr 1688177633Sdfrout: 1689177633Sdfr nlm_release_vfs_state(&vs); 1690177633Sdfr 1691177633Sdfr return (host); 1692177633Sdfr} 1693177633Sdfr 1694177633Sdfrstruct nlm_host * 1695177633Sdfrnlm_do_unlock(nlm4_unlockargs *argp, nlm4_res *result, struct svc_req *rqstp) 1696177633Sdfr{ 1697177633Sdfr fhandle_t fh; 1698177633Sdfr struct vfs_state vs; 1699177633Sdfr struct nlm_host *host; 1700177633Sdfr int error, sysid; 1701177633Sdfr struct flock fl; 1702177633Sdfr 1703177633Sdfr memset(result, 0, sizeof(*result)); 1704177633Sdfr 1705177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1706177633Sdfr if (!host) { 1707177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1708177633Sdfr return (NULL); 1709177633Sdfr } 1710177633Sdfr 1711177633Sdfr if (nlm_debug_level >= 3) 1712177633Sdfr printf("nlm_do_unlock(): caller_name = %s (sysid = %d)\n", 1713177633Sdfr host->nh_caller_name, host->nh_sysid); 1714177633Sdfr 1715177633Sdfr nlm_free_finished_locks(host); 1716177633Sdfr sysid = host->nh_sysid; 1717177633Sdfr 1718177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1719177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1720177633Sdfr 1721177633Sdfr if (time_uptime < nlm_grace_threshold) { 1722177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1723177633Sdfr return (host); 1724177633Sdfr } 1725177633Sdfr 1726177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1727177633Sdfr if (error) { 1728177633Sdfr result->stat.stat = nlm_convert_error(error); 1729177633Sdfr goto out; 1730177633Sdfr } 1731177633Sdfr 1732177633Sdfr fl.l_start = argp->alock.l_offset; 1733177633Sdfr fl.l_len = argp->alock.l_len; 1734177633Sdfr fl.l_pid = argp->alock.svid; 1735177633Sdfr fl.l_sysid = sysid; 1736177633Sdfr fl.l_whence = SEEK_SET; 1737177633Sdfr fl.l_type = F_UNLCK; 1738177633Sdfr error = VOP_ADVLOCK(vs.vs_vp, NULL, F_UNLCK, &fl, F_REMOTE); 1739177633Sdfr 1740177633Sdfr /* 1741177633Sdfr * Ignore the error - there is no result code for failure, 1742177633Sdfr * only for grace period. 1743177633Sdfr */ 1744177633Sdfr result->stat.stat = nlm4_granted; 1745177633Sdfr 1746177633Sdfrout: 1747177633Sdfr nlm_release_vfs_state(&vs); 1748177633Sdfr 1749177633Sdfr return (host); 1750177633Sdfr} 1751177633Sdfr 1752177633Sdfrvoid 1753177633Sdfrnlm_do_free_all(nlm4_notify *argp) 1754177633Sdfr{ 1755177633Sdfr struct nlm_host *host, *thost; 1756177633Sdfr 1757177633Sdfr TAILQ_FOREACH_SAFE(host, &nlm_hosts, nh_link, thost) { 1758177633Sdfr if (!strcmp(host->nh_caller_name, argp->name)) 1759177633Sdfr nlm_host_notify(host, argp->state, FALSE); 1760177633Sdfr } 1761177633Sdfr} 1762177633Sdfr 1763177633Sdfr#define _PATH_RPCLOCKDSOCK "/var/run/rpclockd.sock" 1764177633Sdfr 1765177633Sdfr/* 1766177633Sdfr * Make a connection to the userland lockd - we push anything we can't 1767177633Sdfr * handle out to userland. 1768177633Sdfr */ 1769177633SdfrCLIENT * 1770177633Sdfrnlm_user_lockd(void) 1771177633Sdfr{ 1772177633Sdfr struct sockaddr_un sun; 1773177633Sdfr struct netconfig *nconf; 1774177633Sdfr struct timeval zero; 1775177633Sdfr 1776177633Sdfr if (nlm_lockd) 1777177633Sdfr return (nlm_lockd); 1778177633Sdfr 1779177633Sdfr sun.sun_family = AF_LOCAL; 1780177633Sdfr strcpy(sun.sun_path, _PATH_RPCLOCKDSOCK); 1781177633Sdfr sun.sun_len = SUN_LEN(&sun); 1782177633Sdfr 1783177633Sdfr nconf = getnetconfigent("local"); 1784177633Sdfr nlm_lockd = clnt_reconnect_create(nconf, (struct sockaddr *) &sun, 1785177633Sdfr NLM_PROG, NLM_VERS4, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 1786177633Sdfr 1787177633Sdfr /* 1788177633Sdfr * Set the send timeout to zero - we only use this rpc handle 1789177633Sdfr * for sending async replies which have no return value. 1790177633Sdfr */ 1791177633Sdfr zero.tv_sec = 0; 1792177633Sdfr zero.tv_usec = 0; 1793177633Sdfr CLNT_CONTROL(nlm_lockd, CLSET_TIMEOUT, &zero); 1794177633Sdfr 1795177633Sdfr return (nlm_lockd); 1796177633Sdfr} 1797177662Sdfr 1798177662Sdfr/* 1799177662Sdfr * Kernel module glue 1800177662Sdfr */ 1801177662Sdfrstatic int 1802177662Sdfrnfslockd_modevent(module_t mod, int type, void *data) 1803177662Sdfr{ 1804177662Sdfr 1805177662Sdfr return (0); 1806177662Sdfr} 1807177662Sdfrstatic moduledata_t nfslockd_mod = { 1808177662Sdfr "nfslockd", 1809177662Sdfr nfslockd_modevent, 1810177662Sdfr NULL, 1811177662Sdfr}; 1812177662SdfrDECLARE_MODULE(nfslockd, nfslockd_mod, SI_SUB_VFS, SI_ORDER_ANY); 1813177662Sdfr 1814177662Sdfr/* So that loader and kldload(2) can find us, wherever we are.. */ 1815177662SdfrMODULE_DEPEND(nfslockd, krpc, 1, 1, 1); 1816177662SdfrMODULE_VERSION(nfslockd, 1); 1817