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