Index: lib/libc/gen/Makefile.inc =================================================================== RCS file: /home/ncvs/src/lib/libc/gen/Makefile.inc,v retrieving revision 1.121.2.2 diff -u -r1.121.2.2 Makefile.inc --- lib/libc/gen/Makefile.inc 6 Oct 2007 03:15:15 -0000 1.121.2.2 +++ lib/libc/gen/Makefile.inc 3 Apr 2008 12:52:27 -0000 @@ -4,7 +4,8 @@ # machine-independent gen sources .PATH: ${.CURDIR}/${MACHINE_ARCH}/gen ${.CURDIR}/gen -SRCS+= __xuname.c _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ +SRCS+= __getosreldate.c __xuname.c \ + _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ alarm.c arc4random.c assert.c basename.c check_utility_compat.c \ clock.c closedir.c confstr.c \ crypt.c ctermid.c daemon.c devname.c dirname.c disklabel.c \ Index: lib/libc/gen/__getosreldate.c =================================================================== RCS file: lib/libc/gen/__getosreldate.c diff -N lib/libc/gen/__getosreldate.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libc/gen/__getosreldate.c 3 Apr 2008 12:51:31 -0000 @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2007 Peter Wemm + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +/* + * This is private to libc. It is intended for wrapping syscall stubs in order + * to avoid having to put SIGSYS signal handlers in place to test for presence + * of new syscalls. This caches the result in order to be as quick as possible. + * + * Use getosreldate(3) for public use as it respects the $OSVERSION environment + * variable. + */ + +int +__getosreldate(void) +{ + static int osreldate; + size_t len; + int oid[2]; + int error, osrel; + + if (osreldate != 0) + return (osreldate); + + oid[0] = CTL_KERN; + oid[1] = KERN_OSRELDATE; + osrel = 0; + len = sizeof(osrel); + error = sysctl(oid, 2, &osrel, &len, NULL, 0); + if (error == 0 && osrel > 0 && len == sizeof(osrel)) + osreldate = osrel; + return (osreldate); +} Index: lib/libc/gen/lockf.c =================================================================== RCS file: /home/ncvs/src/lib/libc/gen/lockf.c,v retrieving revision 1.8 diff -u -r1.8 lockf.c --- lib/libc/gen/lockf.c 1 Feb 2002 00:57:29 -0000 1.8 +++ lib/libc/gen/lockf.c 27 Mar 2008 14:45:14 -0000 @@ -74,7 +74,7 @@ fl.l_type = F_WRLCK; if (_fcntl(filedes, F_GETLK, &fl) == -1) return (-1); - if (fl.l_type == F_UNLCK || fl.l_pid == getpid()) + if (fl.l_type == F_UNLCK || (fl.l_sysid == 0 && fl.l_pid == getpid())) return (0); errno = EAGAIN; return (-1); Index: lib/libc/include/libc_private.h =================================================================== RCS file: /home/ncvs/src/lib/libc/include/libc_private.h,v retrieving revision 1.12 diff -u -r1.12 libc_private.h --- lib/libc/include/libc_private.h 15 Aug 2004 16:18:03 -0000 1.12 +++ lib/libc/include/libc_private.h 3 Apr 2008 12:49:41 -0000 @@ -135,4 +135,13 @@ */ extern struct _spinlock *__malloc_lock; +/* + * Get kern.osreldate to detect ABI revisions. Explicitly + * ignores value of $OSVERSION and caches result. + */ +extern int __getosreldate(void); + +/* Without back-compat translation */ +extern int __sys_fcntl(int, int, ...); + #endif /* _LIBC_PRIVATE_H_ */ Index: lib/libc/rpc/svc_vc.c =================================================================== RCS file: /home/ncvs/src/lib/libc/rpc/svc_vc.c,v retrieving revision 1.24 diff -u -r1.24 svc_vc.c --- lib/libc/rpc/svc_vc.c 16 Oct 2004 06:11:35 -0000 1.24 +++ lib/libc/rpc/svc_vc.c 30 Mar 2008 08:37:20 -0000 @@ -602,10 +602,11 @@ if (cd->nonblock) { if (!__xdrrec_getrec(xdrs, &cd->strm_stat, TRUE)) return FALSE; + } else { + (void)xdrrec_skiprecord(xdrs); } xdrs->x_op = XDR_DECODE; - (void)xdrrec_skiprecord(xdrs); if (xdr_callmsg(xdrs, msg)) { cd->x_id = msg->rm_xid; return (TRUE); Index: lib/libc/sys/Makefile.inc =================================================================== RCS file: /home/ncvs/src/lib/libc/sys/Makefile.inc,v retrieving revision 1.113.2.2 diff -u -r1.113.2.2 Makefile.inc --- lib/libc/sys/Makefile.inc 16 Oct 2006 11:59:08 -0000 1.113.2.2 +++ lib/libc/sys/Makefile.inc 3 Apr 2008 14:34:02 -0000 @@ -19,6 +19,10 @@ # Sources common to both syscall interfaces: SRCS+= ftruncate.c lseek.c mmap.c pread.c pwrite.c truncate.c __error.c +.if !defined(WITHOUT_SYSCALL_COMPAT) +SRCS+= fcntl.c +PSEUDO+= _fcntl.o +.endif # Add machine dependent asm sources: SRCS+=${MDASM} Index: lib/libc/sys/fcntl.2 =================================================================== RCS file: /home/ncvs/src/lib/libc/sys/fcntl.2,v retrieving revision 1.44 diff -u -r1.44 fcntl.2 --- lib/libc/sys/fcntl.2 2 Jul 2004 23:52:13 -0000 1.44 +++ lib/libc/sys/fcntl.2 27 Mar 2008 14:45:22 -0000 @@ -181,6 +181,7 @@ pid_t l_pid; /* lock owner */ short l_type; /* lock type: read/write, etc. */ short l_whence; /* type of l_start */ + int l_sysid; /* remote system id or zero for local */ }; .Ed The commands available for advisory record locking are as follows: @@ -268,9 +269,13 @@ means end edge of the region. The .Fa l_pid -field is only used with +and +.Fa l_sysid +fields are only used with .Dv F_GETLK -to return the process ID of the process holding a blocking lock. +to return the process ID of the process holding a blocking lock and +the system ID of the system that owns that process. +Locks created by the local system will have a system ID of zero. After a successful .Dv F_GETLK request, the value of Index: lib/libc/xdr/xdr_rec.c =================================================================== RCS file: /home/ncvs/src/lib/libc/xdr/xdr_rec.c,v retrieving revision 1.21 diff -u -r1.21 xdr_rec.c --- lib/libc/xdr/xdr_rec.c 16 Oct 2004 06:32:43 -0000 1.21 +++ lib/libc/xdr/xdr_rec.c 30 Mar 2008 08:37:21 -0000 @@ -574,6 +574,12 @@ rstrm->in_header &= ~LAST_FRAG; rstrm->last_frag = TRUE; } + /* + * We can only reasonably expect to read once from a + * non-blocking stream. Reading the fragment header + * may have drained the stream. + */ + expectdata = FALSE; } n = rstrm->readit(rstrm->tcp_handle, Index: sys/amd64/conf/GENERIC =================================================================== RCS file: /home/ncvs/src/sys/amd64/conf/GENERIC,v retrieving revision 1.439.2.19 diff -u -r1.439.2.19 GENERIC --- sys/amd64/conf/GENERIC 15 Dec 2007 06:03:43 -0000 1.439.2.19 +++ sys/amd64/conf/GENERIC 27 Mar 2008 14:45:22 -0000 @@ -38,6 +38,7 @@ options MD_ROOT # MD is a potential root device options NFSCLIENT # Network Filesystem Client options NFSSERVER # Network Filesystem Server +options NFSLOCKD # Network Lock Manager options NFS_ROOT # NFS usable as /, requires NFSCLIENT options NTFS # NT File System options MSDOSFS # MSDOS Filesystem Index: sys/arm/conf/AVILA =================================================================== RCS file: /home/ncvs/src/sys/arm/conf/AVILA,v retrieving revision 1.3.2.2 diff -u -r1.3.2.2 AVILA --- sys/arm/conf/AVILA 27 Feb 2007 00:27:50 -0000 1.3.2.2 +++ sys/arm/conf/AVILA 27 Mar 2008 14:45:22 -0000 @@ -57,6 +57,7 @@ options UFS_DIRHASH #Improve performance on big directories options NFSCLIENT #Network Filesystem Client options NFSSERVER #Network Filesystem Server +options NFSLOCKD #Network Lock Manager options NFS_ROOT #NFS usable as /, requires NFSCLIENT #options MSDOSFS #MSDOS Filesystem options CD9660 #ISO 9660 Filesystem Index: sys/arm/conf/HL200 =================================================================== RCS file: /home/ncvs/src/sys/arm/conf/HL200,v retrieving revision 1.2.4.1 diff -u -r1.2.4.1 HL200 --- sys/arm/conf/HL200 2 Dec 2007 14:20:34 -0000 1.2.4.1 +++ sys/arm/conf/HL200 27 Mar 2008 14:45:29 -0000 @@ -49,6 +49,7 @@ #options ROOTDEVNAME=\"ufs:md0\" options NFSCLIENT #Network Filesystem Client #options NFSSERVER #Network Filesystem Server +#options NFSLOCKD #Network Lock Manager options NFS_ROOT #NFS usable as /, requires NFSCLIENT options BOOTP_NFSROOT options BOOTP Index: sys/arm/conf/IQ31244 =================================================================== RCS file: /home/ncvs/src/sys/arm/conf/IQ31244,v retrieving revision 1.11.2.2 diff -u -r1.11.2.2 IQ31244 --- sys/arm/conf/IQ31244 27 Feb 2007 00:27:50 -0000 1.11.2.2 +++ sys/arm/conf/IQ31244 27 Mar 2008 14:45:29 -0000 @@ -46,6 +46,7 @@ options UFS_DIRHASH #Improve performance on big directories options NFSCLIENT #Network Filesystem Client options NFSSERVER #Network Filesystem Server +options NFSLOCKD #Network Lock Manager options NFS_ROOT #NFS usable as /, requires NFSCLIENT #options MSDOSFS #MSDOS Filesystem options CD9660 #ISO 9660 Filesystem Index: sys/arm/conf/SIMICS =================================================================== RCS file: /home/ncvs/src/sys/arm/conf/SIMICS,v retrieving revision 1.7.2.1 diff -u -r1.7.2.1 SIMICS --- sys/arm/conf/SIMICS 23 Jun 2006 16:56:57 -0000 1.7.2.1 +++ sys/arm/conf/SIMICS 27 Mar 2008 14:45:29 -0000 @@ -43,6 +43,7 @@ options ROOTDEVNAME=\"ufs:md0\" options NFSCLIENT #Network Filesystem Client options NFSSERVER #Network Filesystem Server +options NFSLOCKD #Network Lock Manager options NFS_ROOT #NFS usable as /, requires NFSCLIENT #options MSDOSFS #MSDOS Filesystem options CD9660 #ISO 9660 Filesystem Index: sys/arm/conf/SKYEYE =================================================================== RCS file: /home/ncvs/src/sys/arm/conf/SKYEYE,v retrieving revision 1.5.2.1 diff -u -r1.5.2.1 SKYEYE --- sys/arm/conf/SKYEYE 23 Jun 2006 23:35:35 -0000 1.5.2.1 +++ sys/arm/conf/SKYEYE 27 Mar 2008 14:45:29 -0000 @@ -47,6 +47,7 @@ options ROOTDEVNAME=\"ufs:md0\" options NFSCLIENT #Network Filesystem Client options NFSSERVER #Network Filesystem Server +options NFSLOCKD #Network Lock Manager options NFS_ROOT #NFS usable as /, requires NFSCLIENT #options MSDOSFS #MSDOS Filesystem options CD9660 #ISO 9660 Filesystem Index: sys/compat/freebsd32/freebsd32_proto.h =================================================================== RCS file: /home/ncvs/src/sys/compat/freebsd32/freebsd32_proto.h,v retrieving revision 1.38.2.15 diff -u -r1.38.2.15 freebsd32_proto.h --- sys/compat/freebsd32/freebsd32_proto.h 19 Dec 2007 23:04:12 -0000 1.38.2.15 +++ sys/compat/freebsd32/freebsd32_proto.h 28 Mar 2008 14:32:13 -0000 @@ -2,7 +2,7 @@ * System call prototypes. * * DO NOT EDIT-- this file is automatically generated. - * $FreeBSD: src/sys/compat/freebsd32/freebsd32_proto.h,v 1.38.2.15 2007/12/19 23:04:12 jhb Exp $ + * $FreeBSD$ * created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.50.2.15 2007/12/19 23:03:50 jhb Exp */ Index: sys/compat/freebsd32/freebsd32_syscall.h =================================================================== RCS file: /home/ncvs/src/sys/compat/freebsd32/freebsd32_syscall.h,v retrieving revision 1.38.2.15 diff -u -r1.38.2.15 freebsd32_syscall.h --- sys/compat/freebsd32/freebsd32_syscall.h 19 Dec 2007 23:04:12 -0000 1.38.2.15 +++ sys/compat/freebsd32/freebsd32_syscall.h 28 Mar 2008 14:32:12 -0000 @@ -2,7 +2,7 @@ * System call numbers. * * DO NOT EDIT-- this file is automatically generated. - * $FreeBSD: src/sys/compat/freebsd32/freebsd32_syscall.h,v 1.38.2.15 2007/12/19 23:04:12 jhb Exp $ + * $FreeBSD$ * created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.50.2.15 2007/12/19 23:03:50 jhb Exp */ Index: sys/compat/freebsd32/freebsd32_syscalls.c =================================================================== RCS file: /home/ncvs/src/sys/compat/freebsd32/freebsd32_syscalls.c,v retrieving revision 1.29.2.15 diff -u -r1.29.2.15 freebsd32_syscalls.c --- sys/compat/freebsd32/freebsd32_syscalls.c 19 Dec 2007 23:04:12 -0000 1.29.2.15 +++ sys/compat/freebsd32/freebsd32_syscalls.c 28 Mar 2008 14:32:12 -0000 @@ -2,7 +2,7 @@ * System call names. * * DO NOT EDIT-- this file is automatically generated. - * $FreeBSD: src/sys/compat/freebsd32/freebsd32_syscalls.c,v 1.29.2.15 2007/12/19 23:04:12 jhb Exp $ + * $FreeBSD$ * created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.50.2.15 2007/12/19 23:03:50 jhb Exp */ @@ -161,7 +161,7 @@ "#151", /* 151 = sem_lock */ "#152", /* 152 = sem_wakeup */ "#153", /* 153 = asyncdaemon */ - "#154", /* 154 = nosys */ + "#154", /* 154 = nlm_syscall */ "#155", /* 155 = nfssvc */ "obs_ogetdirentries", /* 156 = obsolete ogetdirentries */ "old.freebsd32_statfs", /* 157 = old freebsd32_statfs */ Index: sys/compat/freebsd32/freebsd32_sysent.c =================================================================== RCS file: /home/ncvs/src/sys/compat/freebsd32/freebsd32_sysent.c,v retrieving revision 1.39.2.15 diff -u -r1.39.2.15 freebsd32_sysent.c --- sys/compat/freebsd32/freebsd32_sysent.c 19 Dec 2007 23:04:12 -0000 1.39.2.15 +++ sys/compat/freebsd32/freebsd32_sysent.c 28 Mar 2008 14:32:13 -0000 @@ -2,7 +2,7 @@ * System call switch table. * * DO NOT EDIT-- this file is automatically generated. - * $FreeBSD: src/sys/compat/freebsd32/freebsd32_sysent.c,v 1.39.2.15 2007/12/19 23:04:12 jhb Exp $ + * $FreeBSD$ * created from FreeBSD: src/sys/compat/freebsd32/syscalls.master,v 1.50.2.15 2007/12/19 23:03:50 jhb Exp */ @@ -187,7 +187,7 @@ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 151 = sem_lock */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 152 = sem_wakeup */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 153 = asyncdaemon */ - { 0, (sy_call_t *)nosys, AUE_NULL }, /* 154 = nosys */ + { 0, (sy_call_t *)nosys, AUE_NULL }, /* 154 = nlm_syscall */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 155 = nfssvc */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 156 = obsolete ogetdirentries */ { compat4(SYF_MPSAFE | AS(freebsd4_freebsd32_statfs_args),freebsd32_statfs), AUE_STATFS }, /* 157 = old freebsd32_statfs */ Index: sys/compat/freebsd32/syscalls.master =================================================================== RCS file: /home/ncvs/src/sys/compat/freebsd32/syscalls.master,v retrieving revision 1.50.2.15 diff -u -r1.50.2.15 syscalls.master --- sys/compat/freebsd32/syscalls.master 19 Dec 2007 23:03:50 -0000 1.50.2.15 +++ sys/compat/freebsd32/syscalls.master 27 Mar 2008 14:45:29 -0000 @@ -279,7 +279,8 @@ 151 AUE_NULL UNIMPL sem_lock (BSD/OS 2.x) 152 AUE_NULL UNIMPL sem_wakeup (BSD/OS 2.x) 153 AUE_NULL UNIMPL asyncdaemon (BSD/OS 2.x) -154 AUE_NULL UNIMPL nosys +; 154 is initialised by the NLM code, if present. +154 AUE_NULL UNIMPL nlm_syscall ; 155 is initialized by the NFS code, if present. ; XXX this is a problem!!! 155 AUE_NFS_SVC UNIMPL nfssvc Index: sys/compat/linux/linux_file.c =================================================================== RCS file: /home/ncvs/src/sys/compat/linux/linux_file.c,v retrieving revision 1.91.2.4 diff -u -r1.91.2.4 linux_file.c --- sys/compat/linux/linux_file.c 29 Aug 2007 15:04:25 -0000 1.91.2.4 +++ sys/compat/linux/linux_file.c 27 Mar 2008 14:45:29 -0000 @@ -960,6 +960,8 @@ bsd_flock->l_start = (off_t)linux_flock->l_start; bsd_flock->l_len = (off_t)linux_flock->l_len; bsd_flock->l_pid = (pid_t)linux_flock->l_pid; + bsd_flock->l_sysid = 0; + bsd_flock->l_sysid = 0; } static void Index: sys/compat/svr4/svr4_fcntl.c =================================================================== RCS file: /home/ncvs/src/sys/compat/svr4/svr4_fcntl.c,v retrieving revision 1.35 diff -u -r1.35 svr4_fcntl.c --- sys/compat/svr4/svr4_fcntl.c 7 Feb 2005 21:53:41 -0000 1.35 +++ sys/compat/svr4/svr4_fcntl.c 27 Mar 2008 14:45:29 -0000 @@ -189,7 +189,7 @@ oflp->l_start = (off_t) iflp->l_start; oflp->l_len = (off_t) iflp->l_len; oflp->l_pid = (pid_t) iflp->l_pid; - + oflp->l_sysid = iflp->l_sysid; } static void @@ -215,7 +215,7 @@ oflp->l_whence = (short) iflp->l_whence; oflp->l_start = (svr4_off64_t) iflp->l_start; oflp->l_len = (svr4_off64_t) iflp->l_len; - oflp->l_sysid = 0; + oflp->l_sysid = iflp->l_sysid; oflp->l_pid = (svr4_pid_t) iflp->l_pid; } Index: sys/conf/NOTES =================================================================== RCS file: /home/ncvs/src/sys/conf/NOTES,v retrieving revision 1.1325.2.39 diff -u -r1.1325.2.39 NOTES --- sys/conf/NOTES 12 Mar 2008 11:33:06 -0000 1.1325.2.39 +++ sys/conf/NOTES 27 Mar 2008 14:45:29 -0000 @@ -881,6 +881,7 @@ options HPFS #OS/2 File system options MSDOSFS #MS DOS File System (FAT, FAT32) options NFSSERVER #Network File System server +options NFSLOCKD #Network Lock Manager options NTFS #NT File System options NULLFS #NULL filesystem # Broken (depends on NCP): Index: sys/conf/files =================================================================== RCS file: /home/ncvs/src/sys/conf/files,v retrieving revision 1.1031.2.72 diff -u -r1.1031.2.72 files --- sys/conf/files 19 Mar 2008 01:11:38 -0000 1.1031.2.72 +++ sys/conf/files 27 Mar 2008 17:14:45 -0000 @@ -1893,6 +1893,12 @@ nfsserver/nfs_srvcache.c optional nfsserver nfsserver/nfs_srvsubs.c optional nfsserver nfsserver/nfs_syscalls.c optional nfsserver +nlm/nlm_prot_clnt.c optional nfslockd +nlm/nlm_prot_impl.c optional nfslockd +nlm/nlm_prot_server.c optional nfslockd +nlm/nlm_prot_svc.c optional nfslockd +nlm/nlm_prot_xdr.c optional nfslockd +nlm/sm_inter_xdr.c optional nfslockd # crypto support opencrypto/cast.c optional crypto opencrypto/cast.c optional ipsec ipsec_esp @@ -1932,7 +1938,27 @@ posix4/ksched.c optional _kposix_priority_scheduling posix4/p1003_1b.c standard posix4/posix4_mib.c standard +rpc/auth_none.c optional nfslockd +rpc/auth_unix.c optional nfslockd +rpc/authunix_prot.c optional nfslockd +rpc/clnt_dg.c optional nfslockd +rpc/clnt_rc.c optional nfslockd +rpc/clnt_vc.c optional nfslockd +rpc/getnetconfig.c optional nfslockd +rpc/inet_ntop.c optional nfslockd +rpc/inet_pton.c optional nfslockd +rpc/rpc_callmsg.c optional nfslockd +rpc/rpc_generic.c optional nfslockd +rpc/rpc_prot.c optional nfslockd +rpc/rpcb_clnt.c optional nfslockd +rpc/rpcb_prot.c optional nfslockd rpc/rpcclnt.c optional nfsclient +rpc/svc.c optional nfslockd +rpc/svc_auth.c optional nfslockd +rpc/svc_auth_unix.c optional nfslockd +rpc/svc_dg.c optional nfslockd +rpc/svc_generic.c optional nfslockd +rpc/svc_vc.c optional nfslockd security/audit/audit.c optional audit security/audit/audit_arg.c optional audit security/audit/audit_bsm.c optional audit @@ -2007,3 +2033,9 @@ vm/vm_unix.c standard vm/vm_zeroidle.c standard vm/vnode_pager.c standard +xdr/xdr.c optional nfslockd +xdr/xdr_array.c optional nfslockd +xdr/xdr_mbuf.c optional nfslockd +xdr/xdr_mem.c optional nfslockd +xdr/xdr_reference.c optional nfslockd +xdr/xdr_sizeof.c optional nfslockd \ No newline at end of file Index: sys/conf/options =================================================================== RCS file: /home/ncvs/src/sys/conf/options,v retrieving revision 1.510.2.23 diff -u -r1.510.2.23 options --- sys/conf/options 14 Sep 2007 22:44:37 -0000 1.510.2.23 +++ sys/conf/options 27 Mar 2008 14:45:29 -0000 @@ -389,6 +389,8 @@ TCP_SACK_DEBUG opt_tcp_sack.h TCP_DROP_SYNFIN opt_tcp_input.h XBONEHACK +KRPC +NFSLOCKD # Netgraph(4). Use option NETGRAPH to enable the base netgraph code. # Each netgraph node type can be either be compiled into the kernel Index: sys/fs/msdosfs/msdosfs_vnops.c =================================================================== RCS file: /home/ncvs/src/sys/fs/msdosfs/msdosfs_vnops.c,v retrieving revision 1.160.2.4 diff -u -r1.160.2.4 msdosfs_vnops.c --- sys/fs/msdosfs/msdosfs_vnops.c 8 Jul 2007 15:30:27 -0000 1.160.2.4 +++ sys/fs/msdosfs/msdosfs_vnops.c 27 Mar 2008 14:53:50 -0000 @@ -84,6 +84,7 @@ * Prototypes for MSDOSFS vnode operations */ static vop_advlock_t msdosfs_advlock; +static vop_advlockasync_t msdosfs_advlockasync; static vop_create_t msdosfs_create; static vop_mknod_t msdosfs_mknod; static vop_open_t msdosfs_open; @@ -1889,12 +1890,29 @@ return (lf_advlock(ap, &dep->de_lockf, dep->de_FileSize)); } +static int +msdosfs_advlockasync(ap) + struct vop_advlockasync_args /* { + struct vnode *a_vp; + u_char a_id; + int a_op; + struct flock *a_fl; + int a_flags; + struct task *a_task; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_vp); + + return (lf_advlockasync(ap, &dep->de_lockf, dep->de_FileSize)); +} + /* Global vfs data structures for msdosfs */ struct vop_vector msdosfs_vnodeops = { .vop_default = &default_vnodeops, .vop_access = msdosfs_access, .vop_advlock = msdosfs_advlock, + .vop_advlockasync = msdosfs_advlockasync, .vop_bmap = msdosfs_bmap, .vop_cachedlookup = msdosfs_lookup, .vop_open = msdosfs_open, Index: sys/i386/conf/GENERIC =================================================================== RCS file: /home/ncvs/src/sys/i386/conf/GENERIC,v retrieving revision 1.429.2.15 diff -u -r1.429.2.15 GENERIC --- sys/i386/conf/GENERIC 15 Dec 2007 06:03:43 -0000 1.429.2.15 +++ sys/i386/conf/GENERIC 28 Mar 2008 14:49:41 -0000 @@ -40,6 +40,7 @@ options MD_ROOT # MD is a potential root device options NFSCLIENT # Network Filesystem Client options NFSSERVER # Network Filesystem Server +options NFSLOCKD # Network Lock Manager options NFS_ROOT # NFS usable as /, requires NFSCLIENT options MSDOSFS # MSDOS Filesystem options CD9660 # ISO 9660 Filesystem @@ -58,6 +59,13 @@ options KBD_INSTALL_CDEV # install a CDEV entry in /dev options ADAPTIVE_GIANT # Giant mutex is adaptive. +options KDB +options KDB_TRACE +options KDB_UNATTENDED +options DDB +options DDB_NUMSYM +options GDB + device apic # I/O APIC # Bus support. Index: sys/i386/conf/XBOX =================================================================== RCS file: /home/ncvs/src/sys/i386/conf/XBOX,v retrieving revision 1.7.2.1 diff -u -r1.7.2.1 XBOX --- sys/i386/conf/XBOX 23 Aug 2006 16:28:03 -0000 1.7.2.1 +++ sys/i386/conf/XBOX 27 Mar 2008 14:45:33 -0000 @@ -30,6 +30,7 @@ #options MD_ROOT # MD is a potential root device options NFSCLIENT # Network Filesystem Client #options NFSSERVER # Network Filesystem Server +#options NFSLOCKD # Network Lock Manager #options NFS_ROOT # NFS usable as /, requires NFSCLIENT #options MSDOSFS # MSDOS Filesystem options CD9660 # ISO 9660 Filesystem Index: sys/i386/ibcs2/ibcs2_fcntl.c =================================================================== RCS file: /home/ncvs/src/sys/i386/ibcs2/ibcs2_fcntl.c,v retrieving revision 1.28 diff -u -r1.28 ibcs2_fcntl.c --- sys/i386/ibcs2/ibcs2_fcntl.c 7 Feb 2005 22:02:18 -0000 1.28 +++ sys/i386/ibcs2/ibcs2_fcntl.c 27 Mar 2008 14:45:33 -0000 @@ -93,7 +93,7 @@ iflp->l_whence = (short)flp->l_whence; iflp->l_start = (ibcs2_off_t)flp->l_start; iflp->l_len = (ibcs2_off_t)flp->l_len; - iflp->l_sysid = 0; + iflp->l_sysid = flp->l_sysid; iflp->l_pid = (ibcs2_pid_t)flp->l_pid; } @@ -127,6 +127,7 @@ break; } flp->l_whence = iflp->l_whence; + flp->l_sysid = iflp->l_sysid; } /* convert iBCS2 mode into NetBSD mode */ Index: sys/ia64/conf/GENERIC =================================================================== RCS file: /home/ncvs/src/sys/ia64/conf/GENERIC,v retrieving revision 1.72.2.3 diff -u -r1.72.2.3 GENERIC --- sys/ia64/conf/GENERIC 9 Oct 2006 18:41:36 -0000 1.72.2.3 +++ sys/ia64/conf/GENERIC 27 Mar 2008 14:45:33 -0000 @@ -40,6 +40,7 @@ options MSDOSFS # MSDOS Filesystem options NFSCLIENT # Network Filesystem Client options NFSSERVER # Network Filesystem Server +options NFSLOCKD # Network Lock Manager options NFS_ROOT # NFS usable as root device options PROCFS # Process filesystem (/proc) options PSEUDOFS # Pseudo-filesystem framework Index: sys/kern/init_sysent.c =================================================================== RCS file: /home/ncvs/src/sys/kern/init_sysent.c,v retrieving revision 1.195.2.5 diff -u -r1.195.2.5 init_sysent.c --- sys/kern/init_sysent.c 10 Oct 2006 13:47:59 -0000 1.195.2.5 +++ sys/kern/init_sysent.c 28 Mar 2008 15:08:02 -0000 @@ -2,7 +2,7 @@ * System call switch table. * * DO NOT EDIT-- this file is automatically generated. - * $FreeBSD: src/sys/kern/init_sysent.c,v 1.195.2.5 2006/10/10 13:47:59 rwatson Exp $ + * $FreeBSD$ * created from FreeBSD: src/sys/kern/syscalls.master,v 1.198.2.5 2006/10/10 13:19:47 rwatson Exp */ @@ -183,7 +183,7 @@ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 151 = sem_lock */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 152 = sem_wakeup */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 153 = asyncdaemon */ - { 0, (sy_call_t *)nosys, AUE_NULL }, /* 154 = nosys */ + { SYF_MPSAFE | AS(nlm_syscall_args), (sy_call_t *)lkmressys, AUE_NULL }, /* 154 = nlm_syscall */ { SYF_MPSAFE | AS(nfssvc_args), (sy_call_t *)nosys, AUE_NULL }, /* 155 = nfssvc */ { compat(AS(ogetdirentries_args),getdirentries), AUE_GETDIRENTRIES }, /* 156 = old getdirentries */ { compat4(SYF_MPSAFE | AS(freebsd4_statfs_args),statfs), AUE_STATFS }, /* 157 = old statfs */ Index: sys/kern/kern_descrip.c =================================================================== RCS file: /home/ncvs/src/sys/kern/kern_descrip.c,v retrieving revision 1.279.2.16 diff -u -r1.279.2.16 kern_descrip.c --- sys/kern/kern_descrip.c 14 Feb 2008 11:46:08 -0000 1.279.2.16 +++ sys/kern/kern_descrip.c 27 Mar 2008 16:11:55 -0000 @@ -321,28 +321,67 @@ fcntl(struct thread *td, struct fcntl_args *uap) { struct flock fl; + struct oflock ofl; intptr_t arg; int error; + int cmd; error = 0; + cmd = uap->cmd; switch (uap->cmd) { - case F_GETLK: - case F_SETLK: - case F_SETLKW: - error = copyin((void *)(intptr_t)uap->arg, &fl, sizeof(fl)); + case F_OGETLK: + case F_OSETLK: + case F_OSETLKW: + /* + * Convert old flock structure to new. + */ + error = copyin((void *)(intptr_t)uap->arg, &ofl, sizeof(ofl)); + fl.l_start = ofl.l_start; + fl.l_len = ofl.l_len; + fl.l_pid = ofl.l_pid; + fl.l_type = ofl.l_type; + fl.l_whence = ofl.l_whence; + fl.l_sysid = 0; + + switch (uap->cmd) { + case F_OGETLK: + cmd = F_GETLK; + break; + case F_OSETLK: + cmd = F_SETLK; + break; + case F_OSETLKW: + cmd = F_SETLKW; + break; + } arg = (intptr_t)&fl; break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + case F_SETLK_REMOTE: + error = copyin((void *)(intptr_t)uap->arg, &fl, sizeof(fl)); + arg = (intptr_t)&fl; + break; default: arg = uap->arg; break; } if (error) return (error); - error = kern_fcntl(td, uap->fd, uap->cmd, arg); + error = kern_fcntl(td, uap->fd, cmd, arg); if (error) return (error); - if (uap->cmd == F_GETLK) + if (uap->cmd == F_OGETLK) { + ofl.l_start = fl.l_start; + ofl.l_len = fl.l_len; + ofl.l_pid = fl.l_pid; + ofl.l_type = fl.l_type; + ofl.l_whence = fl.l_whence; + error = copyout(&ofl, (void *)(intptr_t)uap->arg, sizeof(ofl)); + } else if (uap->cmd == F_GETLK) { error = copyout(&fl, (void *)(intptr_t)uap->arg, sizeof(fl)); + } return (error); } @@ -473,12 +512,21 @@ fdrop(fp, td); break; + case F_SETLK_REMOTE: + mtx_assert(&Giant, MA_OWNED); + error = suser(td); + if (error) + return (error); + flg = F_REMOTE; + goto do_setlk; + case F_SETLKW: mtx_assert(&Giant, MA_OWNED); flg |= F_WAIT; /* FALLTHROUGH F_SETLK */ case F_SETLK: + do_setlk: mtx_assert(&Giant, MA_OWNED); if (fp->f_type != DTYPE_VNODE) { FILEDESC_UNLOCK(fdp); @@ -530,7 +578,19 @@ break; case F_UNLCK: error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK, - flp, F_POSIX); + flp, flg); + break; + case F_UNLCKSYS: + /* + * Temporary api for testing remote lock + * infrastructure. + */ + if (flg != F_REMOTE) { + error = EINVAL; + break; + } + error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, + F_UNLCKSYS, flp, flg); break; default: error = EINVAL; Index: sys/kern/kern_lockf.c =================================================================== RCS file: /home/ncvs/src/sys/kern/kern_lockf.c,v retrieving revision 1.54 diff -u -r1.54 kern_lockf.c --- sys/kern/kern_lockf.c 29 Mar 2005 08:13:01 -0000 1.54 +++ sys/kern/kern_lockf.c 31 Mar 2008 08:40:24 -0000 @@ -1,4 +1,30 @@ /*- + * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ + * Authors: Doug Rabson + * Developed with Red Inc: Alfred Perlstein + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * @@ -39,23 +65,20 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include #include - -/* - * This variable controls the maximum number of processes that will - * be checked in doing deadlock detection. - */ -static int maxlockdepth = MAXDEPTH; +#include #ifdef LOCKF_DEBUG #include @@ -63,51 +86,343 @@ #include #include - -static int lockf_debug = 0; +static int lockf_debug = 0; /* control debug output */ SYSCTL_INT(_debug, OID_AUTO, lockf_debug, CTLFLAG_RW, &lockf_debug, 0, ""); #endif MALLOC_DEFINE(M_LOCKF, "lockf", "Byte-range locking structures"); -#define NOLOCKF (struct lockf *)0 +struct owner_edge; +struct owner_vertex; +struct owner_vertex_list; +struct owner_graph; + +#define NOLOCKF (struct lockf_entry *)0 #define SELF 0x1 #define OTHERS 0x2 -static int lf_clearlock(struct lockf *); -static int lf_findoverlap(struct lockf *, - struct lockf *, int, struct lockf ***, struct lockf **); -static struct lockf * - lf_getblock(struct lockf *); -static int lf_getlock(struct lockf *, struct flock *); -static int lf_setlock(struct lockf *); -static void lf_split(struct lockf *, struct lockf *); -static void lf_wakelock(struct lockf *); +static void lf_init(void *); +static int lf_hash_owner(caddr_t, struct flock *, int); +static int lf_owner_matches(struct lock_owner *, caddr_t, struct flock *, + int); +static struct lockf_entry * + lf_alloc_lock(struct lock_owner *); +static void lf_free_lock(struct lockf_entry *); +static int lf_clearlock(struct lockf *, struct lockf_entry *); +static int lf_overlaps(struct lockf_entry *, struct lockf_entry *); +static int lf_blocks(struct lockf_entry *, struct lockf_entry *); +static void lf_free_edge(struct lockf_edge *); +static struct lockf_edge * + lf_alloc_edge(void); +static void lf_alloc_vertex(struct lockf_entry *); +static int lf_add_edge(struct lockf_entry *, struct lockf_entry *); +static void lf_remove_edge(struct lockf_edge *); +static void lf_remove_outgoing(struct lockf_entry *); +static void lf_remove_incoming(struct lockf_entry *); +static int lf_add_outgoing(struct lockf *, struct lockf_entry *); +static int lf_add_incoming(struct lockf *, struct lockf_entry *); +static int lf_findoverlap(struct lockf_entry **, struct lockf_entry *, + int); +static struct lockf_entry * + lf_getblock(struct lockf *, struct lockf_entry *); +static int lf_getlock(struct lockf *, struct lockf_entry *, struct flock *); +static void lf_insert_lock(struct lockf *, struct lockf_entry *); +static void lf_wakeup_lock(struct lockf *, struct lockf_entry *); +static void lf_update_dependancies(struct lockf *, struct lockf_entry *, + int all, struct lockf_entry_list *); +static void lf_set_start(struct lockf *, struct lockf_entry *, off_t, + struct lockf_entry_list*); +static void lf_set_end(struct lockf *, struct lockf_entry *, off_t, + struct lockf_entry_list*); +static int lf_setlock(struct lockf *, struct lockf_entry *, + struct vnode *, void **cookiep); +static int lf_cancel(struct lockf *, struct lockf_entry *, void *); +static void lf_split(struct lockf *, struct lockf_entry *, + struct lockf_entry *, struct lockf_entry_list *); +#ifdef LOCKF_DEBUG +static int graph_reaches(struct owner_vertex *x, struct owner_vertex *y, + struct owner_vertex_list *path); +static void graph_check(struct owner_graph *g, int checkorder); +static void graph_print_vertices(struct owner_vertex_list *set); +#endif +static int graph_delta_forward(struct owner_graph *g, + struct owner_vertex *x, struct owner_vertex *y, + struct owner_vertex_list *delta); +static int graph_delta_backward(struct owner_graph *g, + struct owner_vertex *x, struct owner_vertex *y, + struct owner_vertex_list *delta); +static int graph_add_indices(int *indices, int n, + struct owner_vertex_list *set); +static int graph_assign_indices(struct owner_graph *g, int *indices, + int nextunused, struct owner_vertex_list *set); +static int graph_add_edge(struct owner_graph *g, + struct owner_vertex *x, struct owner_vertex *y); +static void graph_remove_edge(struct owner_graph *g, + struct owner_vertex *x, struct owner_vertex *y); +static struct owner_vertex *graph_alloc_vertex(struct owner_graph *g, + struct lock_owner *lo); +static void graph_free_vertex(struct owner_graph *g, + struct owner_vertex *v); +static struct owner_graph * graph_init(struct owner_graph *g); #ifdef LOCKF_DEBUG -static void lf_print(char *, struct lockf *); -static void lf_printlist(char *, struct lockf *); +static void lf_print(char *, struct lockf_entry *); +static void lf_printlist(char *, struct lockf_entry *); +static void lf_print_owner(struct lock_owner *); #endif /* + * This structure is used to keep track of both local and remote lock + * owners. The lf_owner field of the struct lockf_entry points back at + * the lock owner structure. Each possible lock owner (local proc for + * POSIX fcntl locks, local file for BSD flock locks or + * pair for remote locks) is represented by a unique instance of + * struct lock_owner. + * + * If a lock owner has a lock that blocks some other lock or a lock + * that is waiting for some other lock, it also has a vertex in the + * owner_graph below. + * + * Locks: + * (s) locked by state->ls_lock + * (S) locked by lf_lock_states_lock + * (l) locked by lf_lock_owners_lock + * (g) locked by lf_owner_graph_lock + * (c) const until freeing + */ +#define LOCK_OWNER_HASH_SIZE 256 + +struct lock_owner { + LIST_ENTRY(lock_owner) lo_link; /* (l) hash chain */ + int lo_refs; /* (l) Number of locks referring to this */ + int lo_flags; /* (c) Flags passwd to lf_advlock */ + caddr_t lo_id; /* (c) Id value passed to lf_advlock */ + pid_t lo_pid; /* (c) Process Id of the lock owner */ + int lo_sysid; /* (c) System Id of the lock owner */ + struct owner_vertex *lo_vertex; /* (g) entry in deadlock graph */ +}; + +LIST_HEAD(lock_owner_list, lock_owner); + +static struct sx lf_lock_states_lock; +static struct lockf_list lf_lock_states; /* (S) */ +static struct sx lf_lock_owners_lock; +static struct lock_owner_list lf_lock_owners[LOCK_OWNER_HASH_SIZE]; /* (l) */ + +/* + * Structures for deadlock detection. + * + * We have two types of directed graph, the first is the set of locks, + * both active and pending on a vnode. Within this graph, active locks + * are terminal nodes in the graph (i.e. have no out-going + * edges). Pending locks have out-going edges to each blocking active + * lock that prevents the lock from being granted and also to each + * older pending lock that would block them if it was active. The + * graph for each vnode is naturally acyclic; new edges are only ever + * added to or from new nodes (either new pending locks which only add + * out-going edges or new active locks which only add in-coming edges) + * therefore they cannot create loops in the lock graph. + * + * The second graph is a global graph of lock owners. Each lock owner + * is a vertex in that graph and an edge is added to the graph + * whenever an edge is added to a vnode graph, with end points + * corresponding to owner of the new pending lock and the owner of the + * lock upon which it waits. In order to prevent deadlock, we only add + * an edge to this graph if the new edge would not create a cycle. + * + * The lock owner graph is topologically sorted, i.e. if a node has + * any outgoing edges, then it has an order strictly less than any + * node to which it has an outgoing edge. We preserve this ordering + * (and detect cycles) on edge insertion using Algorithm PK from the + * paper "A Dynamic Topological Sort Algorithm for Directed Acyclic + * Graphs" (ACM Journal of Experimental Algorithms, Vol 11, Article + * No. 1.7) + */ +struct owner_vertex; + +struct owner_edge { + LIST_ENTRY(owner_edge) e_outlink; /* (g) link from's out-edge list */ + LIST_ENTRY(owner_edge) e_inlink; /* (g) link to's in-edge list */ + int e_refs; /* (g) number of times added */ + struct owner_vertex *e_from; /* (c) out-going from here */ + struct owner_vertex *e_to; /* (c) in-coming to here */ +}; +LIST_HEAD(owner_edge_list, owner_edge); + +struct owner_vertex { + TAILQ_ENTRY(owner_vertex) v_link; /* (g) workspace for edge insertion */ + uint32_t v_gen; /* (g) workspace for edge insertion */ + int v_order; /* (g) order of vertex in graph */ + struct owner_edge_list v_outedges;/* (g) list of out-edges */ + struct owner_edge_list v_inedges; /* (g) list of in-edges */ + struct lock_owner *v_owner; /* (c) corresponding lock owner */ +}; +TAILQ_HEAD(owner_vertex_list, owner_vertex); + +struct owner_graph { + struct owner_vertex** g_vertices; /* (g) pointers to vertices */ + int g_size; /* (g) number of vertices */ + int g_space; /* (g) space allocated for vertices */ + int *g_indexbuf; /* (g) workspace for loop detection */ + uint32_t g_gen; /* (g) increment when re-ordering */ +}; + +static struct sx lf_owner_graph_lock; +static struct owner_graph lf_owner_graph; + +/* + * Initialise various structures and locks. + */ +static void +lf_init(void *dummy) +{ + int i; + + sx_init(&lf_lock_states_lock, "lock states lock"); + LIST_INIT(&lf_lock_states); + + sx_init(&lf_lock_owners_lock, "lock owners lock"); + for (i = 0; i < LOCK_OWNER_HASH_SIZE; i++) + LIST_INIT(&lf_lock_owners[i]); + + sx_init(&lf_owner_graph_lock, "owner graph lock"); + graph_init(&lf_owner_graph); +} +SYSINIT(lf_init, SI_SUB_LOCK, SI_ORDER_FIRST, lf_init, NULL); + +/* + * Generate a hash value for a lock owner. + */ +static int +lf_hash_owner(caddr_t id, struct flock *fl, int flags) +{ + uint32_t h; + + if (flags & F_REMOTE) { + h = HASHSTEP(0, fl->l_pid); + h = HASHSTEP(h, fl->l_sysid); + } else if (flags & F_FLOCK) { + h = ((uintptr_t) id) >> 7; + } else { + struct proc *p = (struct proc *) id; + h = HASHSTEP(0, p->p_pid); + h = HASHSTEP(h, 0); + } + + return (h % LOCK_OWNER_HASH_SIZE); +} + +/* + * Return true if a lock owner matches the details passed to + * lf_advlock. + */ +static int +lf_owner_matches(struct lock_owner *lo, caddr_t id, struct flock *fl, + int flags) +{ + if (flags & F_REMOTE) { + return lo->lo_pid == fl->l_pid + && lo->lo_sysid == fl->l_sysid; + } else { + return lo->lo_id == id; + } +} + +static struct lockf_entry * +lf_alloc_lock(struct lock_owner *lo) +{ + struct lockf_entry *lf; + + lf = malloc(sizeof(struct lockf_entry), M_LOCKF, M_WAITOK|M_ZERO); + +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + printf("Allocated lock %p\n", lf); +#endif + if (lo) { + sx_xlock(&lf_lock_owners_lock); + lo->lo_refs++; + sx_xunlock(&lf_lock_owners_lock); + lf->lf_owner = lo; + } + + return (lf); +} + +static void +lf_free_lock(struct lockf_entry *lock) +{ + /* + * Adjust the lock_owner reference count and + * reclaim the entry if this is the last lock + * for that owner. + */ + struct lock_owner *lo = lock->lf_owner; + if (lo) { + KASSERT(LIST_EMPTY(&lock->lf_outedges), + ("freeing lock with dependancies")); + KASSERT(LIST_EMPTY(&lock->lf_inedges), + ("freeing lock with dependants")); + sx_xlock(&lf_lock_owners_lock); + KASSERT(lo->lo_refs > 0, ("lock owner refcount")); + lo->lo_refs--; + if (lo->lo_refs == 0) { +#ifdef LOCKF_DEBUG + if (lockf_debug & 1) + printf("lf_free_lock: freeing lock owner %p\n", + lo); +#endif + if (lo->lo_vertex) { + sx_xlock(&lf_owner_graph_lock); + graph_free_vertex(&lf_owner_graph, + lo->lo_vertex); + sx_xunlock(&lf_owner_graph_lock); + } + LIST_REMOVE(lo, lo_link); + free(lo, M_LOCKF); +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + printf("Freed lock owner %p\n", lo); +#endif + } + sx_unlock(&lf_lock_owners_lock); + } + if ((lock->lf_flags & F_REMOTE) && lock->lf_vnode) { + vrele(lock->lf_vnode); + lock->lf_vnode = NULL; + } +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + printf("Freed lock %p\n", lock); +#endif + free(lock, M_LOCKF); +} + +/* * Advisory record locking support */ int -lf_advlock(ap, head, size) - struct vop_advlock_args /* { - struct vnode *a_vp; - caddr_t a_id; - int a_op; - struct flock *a_fl; - int a_flags; - } */ *ap; - struct lockf **head; - u_quad_t size; +lf_advlockasync(struct vop_advlockasync_args *ap, struct lockf **statep, + u_quad_t size) { - register struct flock *fl = ap->a_fl; - register struct lockf *lock; + struct lockf *state, *freestate = NULL; + struct flock *fl = ap->a_fl; + struct lockf_entry *lock; + struct vnode *vp = ap->a_vp; + caddr_t id = ap->a_id; + int flags = ap->a_flags; + int hash; + struct lock_owner *lo; off_t start, end, oadd; int error; - mtx_lock(&Giant); + /* + * Handle the F_UNLKSYS case first - no need to mess about + * creating a lock owner for this one. + */ + if (ap->a_op == F_UNLCKSYS) { + lf_clearremotesys(fl->l_sysid); + return (0); + } + /* * Convert the flock structure into a start and end. */ @@ -124,59 +439,117 @@ case SEEK_END: if (size > OFF_MAX || - (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) { - error = EOVERFLOW; - goto out; - } + (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) + return (EOVERFLOW); start = size + fl->l_start; break; default: - error = EINVAL; - goto out; - } - if (start < 0) { - error = EINVAL; - goto out; + return (EINVAL); } + if (start < 0) + return (EINVAL); if (fl->l_len < 0) { - if (start == 0) { - error = EINVAL; - goto out; - } + if (start == 0) + return (EINVAL); end = start - 1; start += fl->l_len; - if (start < 0) { - error = EINVAL; - goto out; - } - } else if (fl->l_len == 0) - end = -1; - else { + if (start < 0) + return (EINVAL); + } else if (fl->l_len == 0) { + end = OFF_MAX; + } else { oadd = fl->l_len - 1; - if (oadd > OFF_MAX - start) { - error = EOVERFLOW; - goto out; - } + if (oadd > OFF_MAX - start) + return (EOVERFLOW); end = start + oadd; } /* * Avoid the common case of unlocking when inode has no locks. */ - if (*head == (struct lockf *)0) { + if ((*statep) == NULL || LIST_EMPTY(&(*statep)->ls_active)) { if (ap->a_op != F_SETLK) { fl->l_type = F_UNLCK; - error = 0; - goto out; + return (0); + } + } + + /* + * Map our arguments to an existing lock owner or create one + * if this is the first time we have seen this owner. + */ + hash = lf_hash_owner(id, fl, flags); + sx_xlock(&lf_lock_owners_lock); + LIST_FOREACH(lo, &lf_lock_owners[hash], lo_link) + if (lf_owner_matches(lo, id, fl, flags)) + break; + if (!lo) { + /* + * We initialise the lock with a reference + * count which matches the new lockf_entry + * structure created below. + */ + lo = malloc(sizeof(struct lock_owner), M_LOCKF, + M_WAITOK|M_ZERO); +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + printf("Allocated lock owner %p\n", lo); +#endif + + lo->lo_refs = 1; + lo->lo_flags = flags; + lo->lo_id = id; + if (flags & F_REMOTE) { + lo->lo_pid = fl->l_pid; + lo->lo_sysid = fl->l_sysid; + } else if (flags & F_FLOCK) { + lo->lo_pid = -1; + lo->lo_sysid = 0; + } else { + struct proc *p = (struct proc *) id; + lo->lo_pid = p->p_pid; + lo->lo_sysid = 0; + } + lo->lo_vertex = NULL; + +#ifdef LOCKF_DEBUG + if (lockf_debug & 1) { + printf("lf_advlockasync: new lock owner %p ", lo); + lf_print_owner(lo); + printf("\n"); } +#endif + + LIST_INSERT_HEAD(&lf_lock_owners[hash], lo, lo_link); + } else { + /* + * We have seen this lock owner before, increase its + * reference count to account for the new lockf_entry + * structure we create below. + */ + lo->lo_refs++; } + sx_xunlock(&lf_lock_owners_lock); + /* - * Create the lockf structure + * Create the lockf structure. We initialise the lf_owner + * field here instead of in lf_alloc_lock() to avoid paying + * the lf_lock_owners_lock tax twice. */ - MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK); + lock = lf_alloc_lock(NULL); lock->lf_start = start; lock->lf_end = end; - lock->lf_id = ap->a_id; + lock->lf_owner = lo; + lock->lf_vnode = vp; + if (flags & F_REMOTE) { + /* + * For remote locks, the caller may release its ref to + * the vnode at any time - we have to ref it here to + * prevent it from being recycled unexpectedly. + */ + vref(vp); + } + /* * XXX The problem is that VTOI is ufs specific, so it will * break LOCKF_DEBUG for all other FS's other than UFS because @@ -185,51 +558,703 @@ /* lock->lf_inode = VTOI(ap->a_vp); */ lock->lf_inode = (struct inode *)0; lock->lf_type = fl->l_type; - lock->lf_head = head; - lock->lf_next = (struct lockf *)0; - TAILQ_INIT(&lock->lf_blkhd); + LIST_INIT(&lock->lf_outedges); + LIST_INIT(&lock->lf_inedges); + lock->lf_async_task = ap->a_task; lock->lf_flags = ap->a_flags; + + /* + * Do the requested operation. First find our state structure + * and create a new one if necessary - the caller's *statep + * variable and the state's ls_threads count is protected by + * the vnode interlock. + */ + VI_LOCK(vp); + /* - * Do the requested operation. + * Allocate a state structure if necessary. */ + state = *statep; + if (state == NULL) { + struct lockf *ls; + + VI_UNLOCK(vp); + + ls = malloc(sizeof(struct lockf), M_LOCKF, M_WAITOK|M_ZERO); + sx_init(&ls->ls_lock, "ls_lock"); + LIST_INIT(&ls->ls_active); + LIST_INIT(&ls->ls_pending); + ls->ls_threads = 1; + + sx_xlock(&lf_lock_states_lock); + LIST_INSERT_HEAD(&lf_lock_states, ls, ls_link); + sx_xunlock(&lf_lock_states_lock); + + /* + * Cope if we lost a race with some other thread while + * trying to allocate memory. + */ + VI_LOCK(vp); + if ((*statep) == NULL) { + state = *statep = ls; + VI_UNLOCK(vp); + } else { + state = *statep; + state->ls_threads++; + VI_UNLOCK(vp); + + sx_xlock(&lf_lock_states_lock); + LIST_REMOVE(ls, ls_link); + sx_xunlock(&lf_lock_states_lock); + sx_destroy(&ls->ls_lock); + free(ls, M_LOCKF); + } + } else { + state->ls_threads++; + VI_UNLOCK(vp); + } + + sx_xlock(&state->ls_lock); switch(ap->a_op) { case F_SETLK: - error = lf_setlock(lock); - goto out; + error = lf_setlock(state, lock, vp, ap->a_cookiep); + break; case F_UNLCK: - error = lf_clearlock(lock); - FREE(lock, M_LOCKF); - goto out; + error = lf_clearlock(state, lock); + lf_free_lock(lock); + break; case F_GETLK: - error = lf_getlock(lock, fl); - FREE(lock, M_LOCKF); - goto out; + error = lf_getlock(state, lock, fl); + lf_free_lock(lock); + break; + + case F_CANCEL: + if (ap->a_cookiep) + error = lf_cancel(state, lock, *ap->a_cookiep); + else + error = EINVAL; + lf_free_lock(lock); + break; default: - free(lock, M_LOCKF); + lf_free_lock(lock); error = EINVAL; - goto out; + break; + } + +#ifdef INVARIANTS + /* + * Check for some can't happen stuff. In this case, the active + * lock list becoming disordered or containing mutually + * blocking locks. We also check the pending list for locks + * which should be active (i.e. have no out-going edges). + */ + LIST_FOREACH(lock, &state->ls_active, lf_link) { + struct lockf_entry *lf; + if (LIST_NEXT(lock, lf_link)) + KASSERT((lock->lf_start + <= LIST_NEXT(lock, lf_link)->lf_start), + ("locks disordered")); + LIST_FOREACH(lf, &state->ls_active, lf_link) { + if (lock == lf) + break; + KASSERT(!lf_blocks(lock, lf), + ("two conflicting active locks")); + if (lock->lf_owner == lf->lf_owner) + KASSERT(!lf_overlaps(lock, lf), + ("two overlapping locks from same owner")); + } + } + LIST_FOREACH(lock, &state->ls_pending, lf_link) { + KASSERT(!LIST_EMPTY(&lock->lf_outedges), + ("pending lock which should be active")); + } +#endif + sx_xunlock(&state->ls_lock); + + /* + * If we have removed the last active lock on the vnode and + * this is the last thread that was in-progress, we can free + * the state structure. We update the caller's pointer inside + * the vnode interlock but call free outside. + * + * XXX alternatively, keep the state structure around until + * the filesystem recycles - requires a callback from the + * filesystem. + */ + VI_LOCK(vp); + + state->ls_threads--; + if (LIST_EMPTY(&state->ls_active) && state->ls_threads == 0) { + KASSERT(LIST_EMPTY(&state->ls_pending), + ("freeing state with pending locks")); + freestate = state; + *statep = NULL; + } + + VI_UNLOCK(vp); + + if (freestate) { + sx_xlock(&lf_lock_states_lock); + LIST_REMOVE(freestate, ls_link); + sx_xunlock(&lf_lock_states_lock); + sx_destroy(&freestate->ls_lock); + free(freestate, M_LOCKF); } - /* NOTREACHED */ -out: - mtx_unlock(&Giant); return (error); } +int +lf_advlock(struct vop_advlock_args *ap, struct lockf **statep, u_quad_t size) +{ + struct vop_advlockasync_args a; + + a.a_vp = ap->a_vp; + a.a_id = ap->a_id; + a.a_op = ap->a_op; + a.a_fl = ap->a_fl; + a.a_flags = ap->a_flags; + a.a_task = NULL; + a.a_cookiep = NULL; + + return (lf_advlockasync(&a, statep, size)); +} + +/* + * Return non-zero if locks 'x' and 'y' overlap. + */ +static int +lf_overlaps(struct lockf_entry *x, struct lockf_entry *y) +{ + + return (x->lf_start <= y->lf_end && x->lf_end >= y->lf_start); +} + +/* + * Return non-zero if lock 'x' is blocked by lock 'y' (or vice versa). + */ +static int +lf_blocks(struct lockf_entry *x, struct lockf_entry *y) +{ + + return x->lf_owner != y->lf_owner + && (x->lf_type == F_WRLCK || y->lf_type == F_WRLCK) + && lf_overlaps(x, y); +} + +/* + * Allocate a lock edge from the free list + */ +static struct lockf_edge * +lf_alloc_edge(void) +{ + + return (malloc(sizeof(struct lockf_edge), M_LOCKF, M_WAITOK|M_ZERO)); +} + +/* + * Free a lock edge. + */ +static void +lf_free_edge(struct lockf_edge *e) +{ + + free(e, M_LOCKF); +} + + +/* + * Ensure that the lock's owner has a corresponding vertex in the + * owner graph. + */ +static void +lf_alloc_vertex(struct lockf_entry *lock) +{ + struct owner_graph *g = &lf_owner_graph; + + if (!lock->lf_owner->lo_vertex) + lock->lf_owner->lo_vertex = + graph_alloc_vertex(g, lock->lf_owner); +} + +/* + * Attempt to record an edge from lock x to lock y. Return EDEADLK if + * the new edge would cause a cycle in the owner graph. + */ +static int +lf_add_edge(struct lockf_entry *x, struct lockf_entry *y) +{ + struct owner_graph *g = &lf_owner_graph; + struct lockf_edge *e; + int error; + +#ifdef INVARIANTS + LIST_FOREACH(e, &x->lf_outedges, le_outlink) + KASSERT(e->le_to != y, ("adding lock edge twice")); +#endif + + /* + * Make sure the two owners have entries in the owner graph. + */ + lf_alloc_vertex(x); + lf_alloc_vertex(y); + + error = graph_add_edge(g, x->lf_owner->lo_vertex, + y->lf_owner->lo_vertex); + if (error) + return (error); + + e = lf_alloc_edge(); + LIST_INSERT_HEAD(&x->lf_outedges, e, le_outlink); + LIST_INSERT_HEAD(&y->lf_inedges, e, le_inlink); + e->le_from = x; + e->le_to = y; + + return (0); +} + +/* + * Remove an edge from the lock graph. + */ +static void +lf_remove_edge(struct lockf_edge *e) +{ + struct owner_graph *g = &lf_owner_graph; + struct lockf_entry *x = e->le_from; + struct lockf_entry *y = e->le_to; + + graph_remove_edge(g, x->lf_owner->lo_vertex, y->lf_owner->lo_vertex); + LIST_REMOVE(e, le_outlink); + LIST_REMOVE(e, le_inlink); + e->le_from = NULL; + e->le_to = NULL; + lf_free_edge(e); +} + +/* + * Remove all out-going edges from lock x. + */ +static void +lf_remove_outgoing(struct lockf_entry *x) +{ + struct lockf_edge *e; + + while ((e = LIST_FIRST(&x->lf_outedges)) != NULL) { + lf_remove_edge(e); + } +} + +/* + * Remove all in-coming edges from lock x. + */ +static void +lf_remove_incoming(struct lockf_entry *x) +{ + struct lockf_edge *e; + + while ((e = LIST_FIRST(&x->lf_inedges)) != NULL) { + lf_remove_edge(e); + } +} + +/* + * Walk the list of locks for the file and create an out-going edge + * from lock to each blocking lock. + */ +static int +lf_add_outgoing(struct lockf *state, struct lockf_entry *lock) +{ + struct lockf_entry *overlap; + int error; + + LIST_FOREACH(overlap, &state->ls_active, lf_link) { + /* + * We may assume that the active list is sorted by + * lf_start. + */ + if (overlap->lf_start > lock->lf_end) + break; + if (!lf_blocks(lock, overlap)) + continue; + + /* + * We've found a blocking lock. Add the corresponding + * edge to the graphs and see if it would cause a + * deadlock. + */ + error = lf_add_edge(lock, overlap); + + /* + * The only error that lf_add_edge returns is EDEADLK. + * Remove any edges we added and return the error. + */ + if (error) { + lf_remove_outgoing(lock); + return (error); + } + } + + /* + * We also need to add edges to sleeping locks that block + * us. This ensures that lf_wakeup_lock cannot grant two + * mutually blocking locks simultaneously and also enforces a + * 'first come, first served' fairness model. Note that this + * only happens if we are blocked by at least one active lock + * due to the call to lf_getblock in lf_setlock below. + */ + LIST_FOREACH(overlap, &state->ls_pending, lf_link) { + if (!lf_blocks(lock, overlap)) + continue; + /* + * We've found a blocking lock. Add the corresponding + * edge to the graphs and see if it would cause a + * deadlock. + */ + error = lf_add_edge(lock, overlap); + + /* + * The only error that lf_add_edge returns is EDEADLK. + * Remove any edges we added and return the error. + */ + if (error) { + lf_remove_outgoing(lock); + return (error); + } + } + + return (0); +} + +/* + * Walk the list of pending locks for the file and create an in-coming + * edge from lock to each blocking lock. + */ +static int +lf_add_incoming(struct lockf *state, struct lockf_entry *lock) +{ + struct lockf_entry *overlap; + int error; + + LIST_FOREACH(overlap, &state->ls_pending, lf_link) { + if (!lf_blocks(lock, overlap)) + continue; + + /* + * We've found a blocking lock. Add the corresponding + * edge to the graphs and see if it would cause a + * deadlock. + */ + error = lf_add_edge(overlap, lock); + + /* + * The only error that lf_add_edge returns is EDEADLK. + * Remove any edges we added and return the error. + */ + if (error) { + lf_remove_incoming(lock); + return (error); + } + } + return (0); +} + +/* + * Insert lock into the active list, keeping list entries ordered by + * increasing values of lf_start. + */ +static void +lf_insert_lock(struct lockf *state, struct lockf_entry *lock) +{ + struct lockf_entry *lf, *lfprev; + + if (LIST_EMPTY(&state->ls_active)) { + LIST_INSERT_HEAD(&state->ls_active, lock, lf_link); + return; + } + + lfprev = NULL; + LIST_FOREACH(lf, &state->ls_active, lf_link) { + if (lf->lf_start > lock->lf_start) { + LIST_INSERT_BEFORE(lf, lock, lf_link); + return; + } + lfprev = lf; + } + LIST_INSERT_AFTER(lfprev, lock, lf_link); +} + +/* + * Wake up a sleeping lock and remove it from the pending list now + * that all its dependancies have been resolved. The caller should + * arrange for the lock to be added to the active list, adjusting any + * existing locks for the same owner as needed. + */ +static void +lf_wakeup_lock(struct lockf *state, struct lockf_entry *wakelock) +{ + + /* + * Remove from ls_pending list and wake up the caller + * or start the async notification, as appropriate. + */ + LIST_REMOVE(wakelock, lf_link); +#ifdef LOCKF_DEBUG + if (lockf_debug & 1) + lf_print("lf_wakeup_lock: awakening", wakelock); +#endif /* LOCKF_DEBUG */ + if (wakelock->lf_async_task) { + taskqueue_enqueue(taskqueue_thread, wakelock->lf_async_task); + } else { + wakeup(wakelock); + } +} + +/* + * Re-check all dependant locks and remove edges to locks that we no + * longer block. If 'all' is non-zero, the lock has been removed and + * we must remove all the dependancies, otherwise it has simply been + * reduced but remains active. Any pending locks which have been been + * unblocked are added to 'granted' + */ +static void +lf_update_dependancies(struct lockf *state, struct lockf_entry *lock, int all, + struct lockf_entry_list *granted) +{ + struct lockf_edge *e, *ne; + struct lockf_entry *deplock; + + LIST_FOREACH_SAFE(e, &lock->lf_inedges, le_inlink, ne) { + deplock = e->le_from; + if (all || !lf_blocks(lock, deplock)) { + sx_xlock(&lf_owner_graph_lock); + lf_remove_edge(e); + sx_xunlock(&lf_owner_graph_lock); + if (LIST_EMPTY(&deplock->lf_outedges)) { + lf_wakeup_lock(state, deplock); + LIST_INSERT_HEAD(granted, deplock, lf_link); + } + } + } +} + +/* + * Set the start of an existing active lock, updating dependancies and + * adding any newly woken locks to 'granted'. + */ +static void +lf_set_start(struct lockf *state, struct lockf_entry *lock, off_t new_start, + struct lockf_entry_list *granted) +{ + + KASSERT(new_start >= lock->lf_start, ("can't increase lock")); + lock->lf_start = new_start; + LIST_REMOVE(lock, lf_link); + lf_insert_lock(state, lock); + lf_update_dependancies(state, lock, FALSE, granted); +} + +/* + * Set the end of an existing active lock, updating dependancies and + * adding any newly woken locks to 'granted'. + */ +static void +lf_set_end(struct lockf *state, struct lockf_entry *lock, off_t new_end, + struct lockf_entry_list *granted) +{ + + KASSERT(new_end <= lock->lf_end, ("can't increase lock")); + lock->lf_end = new_end; + lf_update_dependancies(state, lock, FALSE, granted); +} + +/* + * Add a lock to the active list, updating or removing any current + * locks owned by the same owner and processing any pending locks that + * become unblocked as a result. This code is also used for unlock + * since the logic for updating existing locks is identical. + * + * As a result of processing the new lock, we may unblock existing + * pending locks as a result of downgrading/unlocking. We simply + * activate the newly granted locks by looping. + * + * Since the new lock already has its dependancies set up, we always + * add it to the list (unless its an unlock request). This may + * fragment the lock list in some pathological cases but its probably + * not a real problem. + */ +static void +lf_activate_lock(struct lockf *state, struct lockf_entry *lock) +{ + struct lockf_entry *overlap, *lf; + struct lockf_entry_list granted; + int ovcase; + + LIST_INIT(&granted); + LIST_INSERT_HEAD(&granted, lock, lf_link); + + while (!LIST_EMPTY(&granted)) { + lock = LIST_FIRST(&granted); + LIST_REMOVE(lock, lf_link); + + /* + * Skip over locks owned by other processes. Handle + * any locks that overlap and are owned by ourselves. + */ + overlap = LIST_FIRST(&state->ls_active); + for (;;) { + ovcase = lf_findoverlap(&overlap, lock, SELF); + +#ifdef LOCKF_DEBUG + if (ovcase && (lockf_debug & 2)) { + printf("lf_setlock: overlap %d", ovcase); + lf_print("", overlap); + } +#endif + /* + * Six cases: + * 0) no overlap + * 1) overlap == lock + * 2) overlap contains lock + * 3) lock contains overlap + * 4) overlap starts before lock + * 5) overlap ends after lock + */ + switch (ovcase) { + case 0: /* no overlap */ + break; + + case 1: /* overlap == lock */ + /* + * We have already setup the + * dependants for the new lock, taking + * into account a possible downgrade + * or unlock. Remove the old lock. + */ + LIST_REMOVE(overlap, lf_link); + lf_update_dependancies(state, overlap, TRUE, + &granted); + lf_free_lock(overlap); + break; + + case 2: /* overlap contains lock */ + /* + * Just split the existing lock. + */ + lf_split(state, overlap, lock, &granted); + break; + + case 3: /* lock contains overlap */ + /* + * Delete the overlap and advance to + * the next entry in the list. + */ + lf = LIST_NEXT(overlap, lf_link); + LIST_REMOVE(overlap, lf_link); + lf_update_dependancies(state, overlap, TRUE, + &granted); + lf_free_lock(overlap); + overlap = lf; + continue; + + case 4: /* overlap starts before lock */ + /* + * Just update the overlap end and + * move on. + */ + lf_set_end(state, overlap, lock->lf_start - 1, + &granted); + overlap = LIST_NEXT(overlap, lf_link); + continue; + + case 5: /* overlap ends after lock */ + /* + * Change the start of overlap and + * re-insert. + */ + lf_set_start(state, overlap, lock->lf_end + 1, + &granted); + break; + } + break; + } +#ifdef LOCKF_DEBUG + if (lockf_debug & 1) { + if (lock->lf_type != F_UNLCK) + lf_print("lf_activate_lock: activated", lock); + else + lf_print("lf_activate_lock: unlocked", lock); + lf_printlist("lf_activate_lock", lock); + } +#endif /* LOCKF_DEBUG */ + if (lock->lf_type != F_UNLCK) + lf_insert_lock(state, lock); + } +} + +/* + * Cancel a pending lock request, either as a result of a signal or a + * cancel request for an async lock. + */ +static void +lf_cancel_lock(struct lockf *state, struct lockf_entry *lock) +{ + struct lockf_entry_list granted; + + /* + * Note it is theoretically possible that cancelling this lock + * may allow some other pending lock to become + * active. Consider this case: + * + * Owner Action Result Dependancies + * + * A: lock [0..0] succeeds + * B: lock [2..2] succeeds + * C: lock [1..2] blocked C->B + * D: lock [0..1] blocked C->B,D->A,D->C + * A: unlock [0..0] C->B,D->C + * C: cancel [1..2] + */ + + LIST_REMOVE(lock, lf_link); + + /* + * Removing out-going edges is simple. + */ + sx_xlock(&lf_owner_graph_lock); + lf_remove_outgoing(lock); + sx_xunlock(&lf_owner_graph_lock); + + /* + * Removing in-coming edges may allow some other lock to + * become active - we use lf_update_dependancies to figure + * this out. + */ + LIST_INIT(&granted); + lf_update_dependancies(state, lock, TRUE, &granted); + lf_free_lock(lock); + + /* + * Feed any newly active locks to lf_activate_lock. + */ + while (!LIST_EMPTY(&granted)) { + lock = LIST_FIRST(&granted); + LIST_REMOVE(lock, lf_link); + lf_activate_lock(state, lock); + } +} + /* * Set a byte-range lock. */ static int -lf_setlock(lock) - register struct lockf *lock; +lf_setlock(struct lockf *state, struct lockf_entry *lock, struct vnode *vp, + void **cookiep) { - register struct lockf *block; - struct lockf **head = lock->lf_head; - struct lockf **prev, *overlap, *ltmp; + struct lockf_entry *block; static char lockstr[] = "lockf"; - int ovcase, priority, needtolink, error; + int priority, error; #ifdef LOCKF_DEBUG if (lockf_debug & 1) @@ -246,54 +1271,36 @@ /* * Scan lock list for this file looking for locks that would block us. */ - while ((block = lf_getblock(lock))) { + while ((block = lf_getblock(state, lock))) { /* * Free the structure and return if nonblocking. */ - if ((lock->lf_flags & F_WAIT) == 0) { - FREE(lock, M_LOCKF); - return (EAGAIN); + if ((lock->lf_flags & F_WAIT) == 0 + && lock->lf_async_task == NULL) { + lf_free_lock(lock); + error = EAGAIN; + goto out; } + /* - * We are blocked. Since flock style locks cover - * the whole file, there is no chance for deadlock. - * For byte-range locks we must check for deadlock. - * - * Deadlock detection is done by looking through the - * wait channels to see if there are any cycles that - * involve us. MAXDEPTH is set just to make sure we - * do not go off into neverland. - */ - if ((lock->lf_flags & F_POSIX) && - (block->lf_flags & F_POSIX)) { - register struct proc *wproc; - struct thread *td; - register struct lockf *waitblock; - int i = 0; - - /* The block is waiting on something */ - /* XXXKSE this is not complete under threads */ - wproc = (struct proc *)block->lf_id; - mtx_lock_spin(&sched_lock); - FOREACH_THREAD_IN_PROC(wproc, td) { - while (td->td_wchan && - (td->td_wmesg == lockstr) && - (i++ < maxlockdepth)) { - waitblock = (struct lockf *)td->td_wchan; - /* Get the owner of the blocking lock */ - waitblock = waitblock->lf_next; - if ((waitblock->lf_flags & F_POSIX) == 0) - break; - wproc = (struct proc *)waitblock->lf_id; - if (wproc == (struct proc *)lock->lf_id) { - mtx_unlock_spin(&sched_lock); - free(lock, M_LOCKF); - return (EDEADLK); - } - } - } - mtx_unlock_spin(&sched_lock); + * We are blocked. Create edges to each blocking lock, + * checking for deadlock using the owner graph. For + * simplicity, we run deadlock detection for all + * locks, posix and otherwise. + */ + sx_xlock(&lf_owner_graph_lock); + error = lf_add_outgoing(state, lock); + sx_xunlock(&lf_owner_graph_lock); + + if (error) { +#ifdef LOCKF_DEBUG + if (lockf_debug & 1) + lf_print("lf_setlock: deadlock", lock); +#endif + lf_free_lock(lock); + goto out; } + /* * For flock type locks, we must first remove * any shared locks that we hold before we sleep @@ -302,166 +1309,96 @@ if ((lock->lf_flags & F_FLOCK) && lock->lf_type == F_WRLCK) { lock->lf_type = F_UNLCK; - (void) lf_clearlock(lock); + lf_activate_lock(state, lock); lock->lf_type = F_WRLCK; } /* - * Add our lock to the blocked list and sleep until we're free. - * Remember who blocked us (for deadlock detection). + * We have added edges to everything that blocks + * us. Sleep until they all go away. */ - lock->lf_next = block; - TAILQ_INSERT_TAIL(&block->lf_blkhd, lock, lf_block); + LIST_INSERT_HEAD(&state->ls_pending, lock, lf_link); #ifdef LOCKF_DEBUG if (lockf_debug & 1) { - lf_print("lf_setlock: blocking on", block); - lf_printlist("lf_setlock", block); + struct lockf_edge *e; + LIST_FOREACH(e, &lock->lf_outedges, le_outlink) { + lf_print("lf_setlock: blocking on", e->le_to); + lf_printlist("lf_setlock", e->le_to); + } } #endif /* LOCKF_DEBUG */ + + if ((lock->lf_flags & F_WAIT) == 0) { + /* + * The caller requested async notification - + * this callback happens when the blocking + * lock is released, allowing the caller to + * make another attempt to take the lock. + */ + *cookiep = (void *) lock; + error = EINPROGRESS; + goto out; + } + + sx_xunlock(&state->ls_lock); error = tsleep(lock, priority, lockstr, 0); + sx_xlock(&state->ls_lock); /* * We may have been awakened by a signal and/or by a - * debugger continuing us (in which cases we must remove - * ourselves from the blocked list) and/or by another - * process releasing a lock (in which case we have - * already been removed from the blocked list and our - * lf_next field set to NOLOCKF). - */ - if (lock->lf_next) { - TAILQ_REMOVE(&lock->lf_next->lf_blkhd, lock, lf_block); - lock->lf_next = NOLOCKF; + * debugger continuing us (in which cases we must + * remove our lock graph edges) and/or by another + * process releasing a lock (in which case our edges + * have already been removed and we have been moved to + * the active list). + * + * Note that it is possible to receive a signal after + * we were successfully woken (and moved to the active + * list) but before we resumed execution. In this + * case, our lf_outedges list will be clear. We + * pretend there was no error. + * + * Note also, if we have been sleeping long enough, we + * may now have incoming edges from some newer lock + * which is waiting behind us in the queue. + */ + if (LIST_EMPTY(&lock->lf_outedges)) { + error = 0; + } else { + lf_cancel_lock(state, lock); + goto out; } - if (error) { - free(lock, M_LOCKF); - return (error); +#ifdef LOCKF_DEBUG + if (lockf_debug & 1) { + lf_print("lf_setlock: granted", lock); } +#endif + goto out; + } + /* + * It looks like we are going to grant the lock. First add + * edges from any currently pending lock that the new lock + * would block. + */ + sx_xlock(&lf_owner_graph_lock); + error = lf_add_incoming(state, lock); + sx_xunlock(&lf_owner_graph_lock); + if (error) { +#ifdef LOCKF_DEBUG + if (lockf_debug & 1) + lf_print("lf_setlock: deadlock", lock); +#endif + lf_free_lock(lock); + goto out; } + /* * No blocks!! Add the lock. Note that we will * downgrade or upgrade any overlapping locks this * process already owns. - * - * Skip over locks owned by other processes. - * Handle any locks that overlap and are owned by ourselves. */ - prev = head; - block = *head; - needtolink = 1; - for (;;) { - ovcase = lf_findoverlap(block, lock, SELF, &prev, &overlap); - if (ovcase) - block = overlap->lf_next; - /* - * Six cases: - * 0) no overlap - * 1) overlap == lock - * 2) overlap contains lock - * 3) lock contains overlap - * 4) overlap starts before lock - * 5) overlap ends after lock - */ - switch (ovcase) { - case 0: /* no overlap */ - if (needtolink) { - *prev = lock; - lock->lf_next = overlap; - } - break; - - case 1: /* overlap == lock */ - /* - * If downgrading lock, others may be - * able to acquire it. - */ - if (lock->lf_type == F_RDLCK && - overlap->lf_type == F_WRLCK) - lf_wakelock(overlap); - overlap->lf_type = lock->lf_type; - FREE(lock, M_LOCKF); - lock = overlap; /* for debug output below */ - break; - - case 2: /* overlap contains lock */ - /* - * Check for common starting point and different types. - */ - if (overlap->lf_type == lock->lf_type) { - free(lock, M_LOCKF); - lock = overlap; /* for debug output below */ - break; - } - if (overlap->lf_start == lock->lf_start) { - *prev = lock; - lock->lf_next = overlap; - overlap->lf_start = lock->lf_end + 1; - } else - lf_split(overlap, lock); - lf_wakelock(overlap); - break; - - case 3: /* lock contains overlap */ - /* - * If downgrading lock, others may be able to - * acquire it, otherwise take the list. - */ - if (lock->lf_type == F_RDLCK && - overlap->lf_type == F_WRLCK) { - lf_wakelock(overlap); - } else { - while (!TAILQ_EMPTY(&overlap->lf_blkhd)) { - ltmp = TAILQ_FIRST(&overlap->lf_blkhd); - TAILQ_REMOVE(&overlap->lf_blkhd, ltmp, - lf_block); - TAILQ_INSERT_TAIL(&lock->lf_blkhd, - ltmp, lf_block); - ltmp->lf_next = lock; - } - } - /* - * Add the new lock if necessary and delete the overlap. - */ - if (needtolink) { - *prev = lock; - lock->lf_next = overlap->lf_next; - prev = &lock->lf_next; - needtolink = 0; - } else - *prev = overlap->lf_next; - free(overlap, M_LOCKF); - continue; - - case 4: /* overlap starts before lock */ - /* - * Add lock after overlap on the list. - */ - lock->lf_next = overlap->lf_next; - overlap->lf_next = lock; - overlap->lf_end = lock->lf_start - 1; - prev = &lock->lf_next; - lf_wakelock(overlap); - needtolink = 0; - continue; - - case 5: /* overlap ends after lock */ - /* - * Add the new lock before overlap. - */ - if (needtolink) { - *prev = lock; - lock->lf_next = overlap; - } - overlap->lf_start = lock->lf_end + 1; - lf_wakelock(overlap); - break; - } - break; - } -#ifdef LOCKF_DEBUG - if (lockf_debug & 1) { - lf_print("lf_setlock: got the lock", lock); - lf_printlist("lf_setlock", lock); - } -#endif /* LOCKF_DEBUG */ - return (0); + lf_activate_lock(state, lock); + error = 0; +out: + return (error); } /* @@ -471,15 +1408,13 @@ * and remove it (or shrink it), then wakeup anyone we can. */ static int -lf_clearlock(unlock) - register struct lockf *unlock; +lf_clearlock(struct lockf *state, struct lockf_entry *unlock) { - struct lockf **head = unlock->lf_head; - register struct lockf *lf = *head; - struct lockf *overlap, **prev; - int ovcase; + struct lockf_entry *overlap; - if (lf == NOLOCKF) + overlap = LIST_FIRST(&state->ls_active); + + if (overlap == NOLOCKF) return (0); #ifdef LOCKF_DEBUG if (unlock->lf_type != F_UNLCK) @@ -487,82 +1422,36 @@ if (lockf_debug & 1) lf_print("lf_clearlock", unlock); #endif /* LOCKF_DEBUG */ - prev = head; - while ((ovcase = lf_findoverlap(lf, unlock, SELF, &prev, &overlap))) { - /* - * Wakeup the list of locks to be retried. - */ - lf_wakelock(overlap); - - switch (ovcase) { - - case 1: /* overlap == lock */ - *prev = overlap->lf_next; - FREE(overlap, M_LOCKF); - break; - case 2: /* overlap contains lock: split it */ - if (overlap->lf_start == unlock->lf_start) { - overlap->lf_start = unlock->lf_end + 1; - break; - } - lf_split(overlap, unlock); - overlap->lf_next = unlock->lf_next; - break; - - case 3: /* lock contains overlap */ - *prev = overlap->lf_next; - lf = overlap->lf_next; - free(overlap, M_LOCKF); - continue; - - case 4: /* overlap starts before lock */ - overlap->lf_end = unlock->lf_start - 1; - prev = &overlap->lf_next; - lf = overlap->lf_next; - continue; + lf_activate_lock(state, unlock); - case 5: /* overlap ends after lock */ - overlap->lf_start = unlock->lf_end + 1; - break; - } - break; - } -#ifdef LOCKF_DEBUG - if (lockf_debug & 1) - lf_printlist("lf_clearlock", unlock); -#endif /* LOCKF_DEBUG */ return (0); } /* - * Check whether there is a blocking lock, - * and if so return its process identifier. + * Check whether there is a blocking lock, and if so return its + * details in '*fl'. */ static int -lf_getlock(lock, fl) - register struct lockf *lock; - register struct flock *fl; +lf_getlock(struct lockf *state, struct lockf_entry *lock, struct flock *fl) { - register struct lockf *block; + struct lockf_entry *block; #ifdef LOCKF_DEBUG if (lockf_debug & 1) lf_print("lf_getlock", lock); #endif /* LOCKF_DEBUG */ - if ((block = lf_getblock(lock))) { + if ((block = lf_getblock(state, lock))) { fl->l_type = block->lf_type; fl->l_whence = SEEK_SET; fl->l_start = block->lf_start; - if (block->lf_end == -1) + if (block->lf_end == OFF_MAX) fl->l_len = 0; else fl->l_len = block->lf_end - block->lf_start + 1; - if (block->lf_flags & F_POSIX) - fl->l_pid = ((struct proc *)(block->lf_id))->p_pid; - else - fl->l_pid = -1; + fl->l_pid = block->lf_owner->lo_pid; + fl->l_sysid = block->lf_owner->lo_sysid; } else { fl->l_type = F_UNLCK; } @@ -570,63 +1459,129 @@ } /* + * Cancel an async lock request. + */ +static int +lf_cancel(struct lockf *state, struct lockf_entry *lock, void *cookie) +{ + struct lockf_entry *reallock; + + /* + * We need to match this request with an existing lock + * request. + */ + LIST_FOREACH(reallock, &state->ls_pending, lf_link) { + if ((void *) reallock == cookie) { + /* + * Double-check that this lock looks right + * (maybe use a rolling ID for the cancel + * cookie instead?) + */ + if (!(reallock->lf_vnode == lock->lf_vnode + && reallock->lf_start == lock->lf_start + && reallock->lf_end == lock->lf_end)) { + return (ENOENT); + } + + /* + * Make sure this lock was async and then just + * remove it from its wait lists. + */ + if (!reallock->lf_async_task) { + return (ENOENT); + } + + /* + * Note that since any other thread must take + * state->ls_lock before it can possibly + * trigger the async callback, we are safe + * from a race with lf_wakeup_lock, i.e. we + * can free the lock (actually our caller does + * this). + */ + lf_cancel_lock(state, reallock); + return (0); + } + } + + /* + * We didn't find a matching lock - not much we can do here. + */ + return (ENOENT); +} + +/* * Walk the list of locks for an inode and * return the first blocking lock. */ -static struct lockf * -lf_getblock(lock) - register struct lockf *lock; +static struct lockf_entry * +lf_getblock(struct lockf *state, struct lockf_entry *lock) { - struct lockf **prev, *overlap, *lf = *(lock->lf_head); - int ovcase; + struct lockf_entry *overlap; - prev = lock->lf_head; - while ((ovcase = lf_findoverlap(lf, lock, OTHERS, &prev, &overlap))) { + LIST_FOREACH(overlap, &state->ls_active, lf_link) { /* - * We've found an overlap, see if it blocks us + * We may assume that the active list is sorted by + * lf_start. */ - if ((lock->lf_type == F_WRLCK || overlap->lf_type == F_WRLCK)) - return (overlap); - /* - * Nope, point to the next one on the list and - * see if it blocks us - */ - lf = overlap->lf_next; + if (overlap->lf_start > lock->lf_end) + break; + if (!lf_blocks(lock, overlap)) + continue; + return (overlap); } return (NOLOCKF); } /* - * Walk the list of locks for an inode to - * find an overlapping lock (if any). + * Walk the list of locks for an inode to find an overlapping lock (if + * any) and return a classification of that overlap. + * + * Arguments: + * *overlap The place in the lock list to start looking + * lock The lock which is being tested + * type Pass 'SELF' to test only locks with the same + * owner as lock, or 'OTHER' to test only locks + * with a different owner + * + * Returns one of six values: + * 0) no overlap + * 1) overlap == lock + * 2) overlap contains lock + * 3) lock contains overlap + * 4) overlap starts before lock + * 5) overlap ends after lock + * + * If there is an overlapping lock, '*overlap' is set to point at the + * overlapping lock. * * NOTE: this returns only the FIRST overlapping lock. There * may be more than one. */ static int -lf_findoverlap(lf, lock, type, prev, overlap) - register struct lockf *lf; - struct lockf *lock; - int type; - struct lockf ***prev; - struct lockf **overlap; +lf_findoverlap(struct lockf_entry **overlap, struct lockf_entry *lock, int type) { + struct lockf_entry *lf; off_t start, end; + int res; - *overlap = lf; - if (lf == NOLOCKF) + if ((*overlap) == NOLOCKF) { return (0); + } #ifdef LOCKF_DEBUG if (lockf_debug & 2) lf_print("lf_findoverlap: looking for overlap in", lock); #endif /* LOCKF_DEBUG */ start = lock->lf_start; end = lock->lf_end; - while (lf != NOLOCKF) { - if (((type & SELF) && lf->lf_id != lock->lf_id) || - ((type & OTHERS) && lf->lf_id == lock->lf_id)) { - *prev = &lf->lf_next; - *overlap = lf = lf->lf_next; + res = 0; + while (*overlap) { + lf = *overlap; + if (lf->lf_start > end) + break; + if (((type & SELF) && lf->lf_owner != lock->lf_owner) || + ((type & OTHERS) && lf->lf_owner == lock->lf_owner)) { + *overlap = LIST_NEXT(lf, lf_link); continue; } #ifdef LOCKF_DEBUG @@ -644,81 +1599,78 @@ * 4) overlap starts before lock * 5) overlap ends after lock */ - if ((lf->lf_end != -1 && start > lf->lf_end) || - (end != -1 && lf->lf_start > end)) { + if (start > lf->lf_end) { /* Case 0 */ #ifdef LOCKF_DEBUG if (lockf_debug & 2) printf("no overlap\n"); #endif /* LOCKF_DEBUG */ - if ((type & SELF) && end != -1 && lf->lf_start > end) - return (0); - *prev = &lf->lf_next; - *overlap = lf = lf->lf_next; + *overlap = LIST_NEXT(lf, lf_link); continue; } - if ((lf->lf_start == start) && (lf->lf_end == end)) { + if (lf->lf_start == start && lf->lf_end == end) { /* Case 1 */ #ifdef LOCKF_DEBUG if (lockf_debug & 2) printf("overlap == lock\n"); #endif /* LOCKF_DEBUG */ - return (1); + res = 1; + break; } - if ((lf->lf_start <= start) && - (end != -1) && - ((lf->lf_end >= end) || (lf->lf_end == -1))) { + if (lf->lf_start <= start && lf->lf_end >= end) { /* Case 2 */ #ifdef LOCKF_DEBUG if (lockf_debug & 2) printf("overlap contains lock\n"); #endif /* LOCKF_DEBUG */ - return (2); + res = 2; + break; } - if (start <= lf->lf_start && - (end == -1 || - (lf->lf_end != -1 && end >= lf->lf_end))) { + if (start <= lf->lf_start && end >= lf->lf_end) { /* Case 3 */ #ifdef LOCKF_DEBUG if (lockf_debug & 2) printf("lock contains overlap\n"); #endif /* LOCKF_DEBUG */ - return (3); + res = 3; + break; } - if ((lf->lf_start < start) && - ((lf->lf_end >= start) || (lf->lf_end == -1))) { + if (lf->lf_start < start && lf->lf_end >= start) { /* Case 4 */ #ifdef LOCKF_DEBUG if (lockf_debug & 2) printf("overlap starts before lock\n"); #endif /* LOCKF_DEBUG */ - return (4); + res = 4; + break; } - if ((lf->lf_start > start) && - (end != -1) && - ((lf->lf_end > end) || (lf->lf_end == -1))) { + if (lf->lf_start > start && lf->lf_end > end) { /* Case 5 */ #ifdef LOCKF_DEBUG if (lockf_debug & 2) printf("overlap ends after lock\n"); #endif /* LOCKF_DEBUG */ - return (5); + res = 5; + break; } panic("lf_findoverlap: default"); } - return (0); + return (res); } /* - * Split a lock and a contained region into - * two or three locks as necessary. + * Split an the existing 'lock1', based on the extent of the lock + * described by 'lock2'. The existing lock should cover 'lock2' + * entirely. + * + * Any pending locks which have been been unblocked are added to + * 'granted' */ static void -lf_split(lock1, lock2) - register struct lockf *lock1; - register struct lockf *lock2; +lf_split(struct lockf *state, struct lockf_entry *lock1, + struct lockf_entry *lock2, struct lockf_entry_list *granted) { - register struct lockf *splitlock; + struct lockf_entry *splitlock; #ifdef LOCKF_DEBUG if (lockf_debug & 2) { @@ -727,98 +1679,616 @@ } #endif /* LOCKF_DEBUG */ /* - * Check to see if spliting into only two pieces. + * Check to see if we don't need to split at all. */ if (lock1->lf_start == lock2->lf_start) { - lock1->lf_start = lock2->lf_end + 1; - lock2->lf_next = lock1; + lf_set_start(state, lock1, lock2->lf_end + 1, granted); return; } if (lock1->lf_end == lock2->lf_end) { - lock1->lf_end = lock2->lf_start - 1; - lock2->lf_next = lock1->lf_next; - lock1->lf_next = lock2; + lf_set_end(state, lock1, lock2->lf_start - 1, granted); return; } /* * Make a new lock consisting of the last part of - * the encompassing lock + * the encompassing lock. + */ + splitlock = lf_alloc_lock(lock1->lf_owner); + memcpy(splitlock, lock1, sizeof *splitlock); + if (splitlock->lf_flags & F_REMOTE) + vref(splitlock->lf_vnode); + + /* + * This cannot cause a deadlock since any edges we would add + * to splitlock already exist in lock1. We must be sure to add + * necessary dependancies to splitlock before we reduce lock1 + * otherwise we may accidentally grant a pending lock that + * was blocked by the tail end of lock1. */ - MALLOC(splitlock, struct lockf *, sizeof *splitlock, M_LOCKF, M_WAITOK); - bcopy(lock1, splitlock, sizeof *splitlock); splitlock->lf_start = lock2->lf_end + 1; - TAILQ_INIT(&splitlock->lf_blkhd); - lock1->lf_end = lock2->lf_start - 1; + LIST_INIT(&splitlock->lf_outedges); + LIST_INIT(&splitlock->lf_inedges); + sx_xlock(&lf_owner_graph_lock); + lf_add_incoming(state, splitlock); + sx_xunlock(&lf_owner_graph_lock); + + lf_set_end(state, lock1, lock2->lf_start - 1, granted); + /* * OK, now link it in */ - splitlock->lf_next = lock1->lf_next; - lock2->lf_next = splitlock; - lock1->lf_next = lock2; + lf_insert_lock(state, splitlock); +} + +struct clearlock { + STAILQ_ENTRY(clearlock) link; + struct vnode *vp; + struct flock fl; +}; +STAILQ_HEAD(clearlocklist, clearlock); + +void +lf_clearremotesys(int sysid) +{ + struct lockf *ls; + struct lockf_entry *lf; + struct clearlock *cl; + struct clearlocklist locks; + + KASSERT(sysid != 0, ("Can't clear local locks with F_UNLCKSYS")); + + /* + * In order to keep the locking simple, we iterate over the + * active lock lists to build a list of locks that need + * releasing. We then call VOP_ADVLOCK for each one in turn. + * + * We take an extra reference to the vnode for the duration to + * make sure it doesn't go away before we are finished. + */ + STAILQ_INIT(&locks); + sx_xlock(&lf_lock_states_lock); + LIST_FOREACH(ls, &lf_lock_states, ls_link) { + sx_xlock(&ls->ls_lock); + LIST_FOREACH(lf, &ls->ls_active, lf_link) { + if (lf->lf_owner->lo_sysid != sysid) + continue; + + cl = malloc(sizeof(struct clearlock), M_LOCKF, + M_WAITOK); + cl->vp = lf->lf_vnode; + vref(cl->vp); + cl->fl.l_start = lf->lf_start; + if (lf->lf_end == OFF_MAX) + cl->fl.l_len = 0; + else + cl->fl.l_len = + lf->lf_end - lf->lf_start + 1; + cl->fl.l_whence = SEEK_SET; + cl->fl.l_type = F_UNLCK; + cl->fl.l_pid = lf->lf_owner->lo_pid; + cl->fl.l_sysid = sysid; + STAILQ_INSERT_TAIL(&locks, cl, link); + } + sx_xunlock(&ls->ls_lock); + } + sx_xunlock(&lf_lock_states_lock); + + while ((cl = STAILQ_FIRST(&locks)) != NULL) { + STAILQ_REMOVE_HEAD(&locks, link); + VOP_ADVLOCK(cl->vp, 0, F_UNLCK, &cl->fl, F_REMOTE); + vrele(cl->vp); + free(cl, M_LOCKF); + } +} + +int +lf_countlocks(int sysid) +{ + int i; + struct lock_owner *lo; + int count; + + count = 0; + sx_xlock(&lf_lock_owners_lock); + for (i = 0; i < LOCK_OWNER_HASH_SIZE; i++) + LIST_FOREACH(lo, &lf_lock_owners[i], lo_link) + if (lo->lo_sysid == sysid) + count += lo->lo_refs; + sx_xunlock(&lf_lock_owners_lock); + + return (count); +} + +#ifdef LOCKF_DEBUG + +/* + * Return non-zero if y is reachable from x using a brute force + * search. If reachable and path is non-null, return the route taken + * in path. + */ +static int +graph_reaches(struct owner_vertex *x, struct owner_vertex *y, + struct owner_vertex_list *path) +{ + struct owner_edge *e; + + if (x == y) { + if (path) + TAILQ_INSERT_HEAD(path, x, v_link); + return 1; + } + + LIST_FOREACH(e, &x->v_outedges, e_outlink) { + if (graph_reaches(e->e_to, y, path)) { + if (path) + TAILQ_INSERT_HEAD(path, x, v_link); + return 1; + } + } + return 0; +} + +/* + * Perform consistency checks on the graph. Make sure the values of + * v_order are correct. If checkorder is non-zero, check no vertex can + * reach any other vertex with a smaller order. + */ +static void +graph_check(struct owner_graph *g, int checkorder) +{ + int i, j; + + for (i = 0; i < g->g_size; i++) { + if (!g->g_vertices[i]->v_owner) + continue; + KASSERT(g->g_vertices[i]->v_order == i, + ("lock graph vertices disordered")); + if (checkorder) { + for (j = 0; j < i; j++) { + if (!g->g_vertices[j]->v_owner) + continue; + KASSERT(!graph_reaches(g->g_vertices[i], + g->g_vertices[j], NULL), + ("lock graph vertices disordered")); + } + } + } +} + +static void +graph_print_vertices(struct owner_vertex_list *set) +{ + struct owner_vertex *v; + + printf("{ "); + TAILQ_FOREACH(v, set, v_link) { + printf("%d:", v->v_order); + lf_print_owner(v->v_owner); + if (TAILQ_NEXT(v, v_link)) + printf(", "); + } + printf(" }\n"); +} + +#endif + +/* + * Calculate the sub-set of vertices v from the affected region [y..x] + * where v is reachable from y. Return -1 if a loop was detected + * (i.e. x is reachable from y, otherwise the number of vertices in + * this subset. + */ +static int +graph_delta_forward(struct owner_graph *g, struct owner_vertex *x, + struct owner_vertex *y, struct owner_vertex_list *delta) +{ + uint32_t gen; + struct owner_vertex *v; + struct owner_edge *e; + int n; + + /* + * We start with a set containing just y. Then for each vertex + * v in the set so far unprocessed, we add each vertex that v + * has an out-edge to and that is within the affected region + * [y..x]. If we see the vertex x on our travels, stop + * immediately. + */ + TAILQ_INIT(delta); + TAILQ_INSERT_TAIL(delta, y, v_link); + v = y; + n = 1; + gen = g->g_gen; + while (v) { + LIST_FOREACH(e, &v->v_outedges, e_outlink) { + if (e->e_to == x) + return -1; + if (e->e_to->v_order < x->v_order + && e->e_to->v_gen != gen) { + e->e_to->v_gen = gen; + TAILQ_INSERT_TAIL(delta, e->e_to, v_link); + n++; + } + } + v = TAILQ_NEXT(v, v_link); + } + + return (n); +} + +/* + * Calculate the sub-set of vertices v from the affected region [y..x] + * where v reaches x. Return the number of vertices in this subset. + */ +static int +graph_delta_backward(struct owner_graph *g, struct owner_vertex *x, + struct owner_vertex *y, struct owner_vertex_list *delta) +{ + uint32_t gen; + struct owner_vertex *v; + struct owner_edge *e; + int n; + + /* + * We start with a set containing just x. Then for each vertex + * v in the set so far unprocessed, we add each vertex that v + * has an in-edge from and that is within the affected region + * [y..x]. + */ + TAILQ_INIT(delta); + TAILQ_INSERT_TAIL(delta, x, v_link); + v = x; + n = 1; + gen = g->g_gen; + while (v) { + LIST_FOREACH(e, &v->v_inedges, e_inlink) { + if (e->e_from->v_order > y->v_order + && e->e_from->v_gen != gen) { + e->e_from->v_gen = gen; + TAILQ_INSERT_HEAD(delta, e->e_from, v_link); + n++; + } + } + v = TAILQ_PREV(v, owner_vertex_list, v_link); + } + + return (n); +} + +static int +graph_add_indices(int *indices, int n, struct owner_vertex_list *set) +{ + struct owner_vertex *v; + int i, j; + + TAILQ_FOREACH(v, set, v_link) { + for (i = n; + i > 0 && indices[i - 1] > v->v_order; i--) + ; + for (j = n - 1; j >= i; j--) + indices[j + 1] = indices[j]; + indices[i] = v->v_order; + n++; + } + + return (n); +} + +static int +graph_assign_indices(struct owner_graph *g, int *indices, int nextunused, + struct owner_vertex_list *set) +{ + struct owner_vertex *v, *vlowest; + + while (!TAILQ_EMPTY(set)) { + vlowest = NULL; + TAILQ_FOREACH(v, set, v_link) { + if (!vlowest || v->v_order < vlowest->v_order) + vlowest = v; + } + TAILQ_REMOVE(set, vlowest, v_link); + vlowest->v_order = indices[nextunused]; + g->g_vertices[vlowest->v_order] = vlowest; + nextunused++; + } + + return (nextunused); +} + +static int +graph_add_edge(struct owner_graph *g, struct owner_vertex *x, + struct owner_vertex *y) +{ + struct owner_edge *e; + struct owner_vertex_list deltaF, deltaB; + int nF, nB, n, vi, i; + int *indices; + + sx_assert(&lf_owner_graph_lock, SX_XLOCKED); + + LIST_FOREACH(e, &x->v_outedges, e_outlink) { + if (e->e_to == y) { + e->e_refs++; + return (0); + } + } + +#ifdef LOCKF_DEBUG + if (lockf_debug & 8) { + printf("adding edge %d:", x->v_order); + lf_print_owner(x->v_owner); + printf(" -> %d:", y->v_order); + lf_print_owner(y->v_owner); + printf("\n"); + } +#endif + if (y->v_order < x->v_order) { + /* + * The new edge violates the order. First find the set + * of affected vertices reachable from y (deltaF) and + * the set of affect vertices affected that reach x + * (deltaB), using the graph generation number to + * detect whether we have visited a given vertex + * already. We re-order the graph so that each vertex + * in deltaB appears before each vertex in deltaF. + * + * If x is a member of deltaF, then the new edge would + * create a cycle. Otherwise, we may assume that + * deltaF and deltaB are disjoint. + */ + g->g_gen++; + if (g->g_gen == 0) { + /* + * Generation wrap. + */ + for (vi = 0; vi < g->g_size; vi++) { + g->g_vertices[vi]->v_gen = 0; + } + g->g_gen++; + } + nF = graph_delta_forward(g, x, y, &deltaF); + if (nF < 0) { +#ifdef LOCKF_DEBUG + if (lockf_debug & 8) { + struct owner_vertex_list path; + printf("deadlock: "); + TAILQ_INIT(&path); + graph_reaches(y, x, &path); + graph_print_vertices(&path); + } +#endif + return (EDEADLK); + } + +#ifdef LOCKF_DEBUG + if (lockf_debug & 8) { + printf("re-ordering graph vertices\n"); + printf("deltaF = "); + graph_print_vertices(&deltaF); + } +#endif + + nB = graph_delta_backward(g, x, y, &deltaB); + +#ifdef LOCKF_DEBUG + if (lockf_debug & 8) { + printf("deltaB = "); + graph_print_vertices(&deltaB); + } +#endif + + /* + * We first build a set of vertex indices (vertex + * order values) that we may use, then we re-assign + * orders first to those vertices in deltaB, then to + * deltaF. Note that the contents of deltaF and deltaB + * may be partially disordered - we perform an + * insertion sort while building our index set. + */ + indices = g->g_indexbuf; + n = graph_add_indices(indices, 0, &deltaF); + graph_add_indices(indices, n, &deltaB); + + /* + * We must also be sure to maintain the relative + * ordering of deltaF and deltaB when re-assigning + * vertices. We do this by iteratively removing the + * lowest ordered element from the set and assigning + * it the next value from our new ordering. + */ + i = graph_assign_indices(g, indices, 0, &deltaB); + graph_assign_indices(g, indices, i, &deltaF); + +#ifdef LOCKF_DEBUG + if (lockf_debug & 8) { + struct owner_vertex_list set; + TAILQ_INIT(&set); + for (i = 0; i < nB + nF; i++) + TAILQ_INSERT_TAIL(&set, + g->g_vertices[indices[i]], v_link); + printf("new ordering = "); + graph_print_vertices(&set); + } +#endif + } + + KASSERT(x->v_order < y->v_order, ("Failed to re-order graph")); + +#ifdef LOCKF_DEBUG + if (lockf_debug & 8) { + graph_check(g, TRUE); + } +#endif + + e = malloc(sizeof(struct owner_edge), M_LOCKF, M_WAITOK); + + LIST_INSERT_HEAD(&x->v_outedges, e, e_outlink); + LIST_INSERT_HEAD(&y->v_inedges, e, e_inlink); + e->e_refs = 1; + e->e_from = x; + e->e_to = y; + + return (0); } /* - * Wakeup a blocklist + * Remove an edge x->y from the graph. */ static void -lf_wakelock(listhead) - struct lockf *listhead; +graph_remove_edge(struct owner_graph *g, struct owner_vertex *x, + struct owner_vertex *y) { - register struct lockf *wakelock; + struct owner_edge *e; + + sx_assert(&lf_owner_graph_lock, SX_XLOCKED); - while (!TAILQ_EMPTY(&listhead->lf_blkhd)) { - wakelock = TAILQ_FIRST(&listhead->lf_blkhd); - TAILQ_REMOVE(&listhead->lf_blkhd, wakelock, lf_block); - wakelock->lf_next = NOLOCKF; + LIST_FOREACH(e, &x->v_outedges, e_outlink) { + if (e->e_to == y) + break; + } + KASSERT(e, ("Removing non-existent edge from deadlock graph")); + + e->e_refs--; + if (e->e_refs == 0) { #ifdef LOCKF_DEBUG - if (lockf_debug & 2) - lf_print("lf_wakelock: awakening", wakelock); -#endif /* LOCKF_DEBUG */ - wakeup(wakelock); + if (lockf_debug & 8) { + printf("removing edge %d:", x->v_order); + lf_print_owner(x->v_owner); + printf(" -> %d:", y->v_order); + lf_print_owner(y->v_owner); + printf("\n"); + } +#endif + LIST_REMOVE(e, e_outlink); + LIST_REMOVE(e, e_inlink); + free(e, M_LOCKF); + } +} + +/* + * Allocate a vertex from the free list. Return ENOMEM if there are + * none. + */ +static struct owner_vertex * +graph_alloc_vertex(struct owner_graph *g, struct lock_owner *lo) +{ + struct owner_vertex *v; + + sx_assert(&lf_owner_graph_lock, SX_XLOCKED); + + v = malloc(sizeof(struct owner_vertex), M_LOCKF, M_WAITOK); + if (g->g_size == g->g_space) { + g->g_vertices = realloc(g->g_vertices, + 2 * g->g_space * sizeof(struct owner_vertex *), + M_LOCKF, M_WAITOK); + free(g->g_indexbuf, M_LOCKF); + g->g_indexbuf = malloc(2 * g->g_space * sizeof(int), + M_LOCKF, M_WAITOK); + g->g_space = 2 * g->g_space; + } + v->v_order = g->g_size; + v->v_gen = g->g_gen; + g->g_vertices[g->g_size] = v; + g->g_size++; + + LIST_INIT(&v->v_outedges); + LIST_INIT(&v->v_inedges); + v->v_owner = lo; + + return (v); +} + +static void +graph_free_vertex(struct owner_graph *g, struct owner_vertex *v) +{ + struct owner_vertex *w; + int i; + + sx_assert(&lf_owner_graph_lock, SX_XLOCKED); + + KASSERT(LIST_EMPTY(&v->v_outedges), ("Freeing vertex with edges")); + KASSERT(LIST_EMPTY(&v->v_inedges), ("Freeing vertex with edges")); + + /* + * Remove from the graph's array and close up the gap, + * renumbering the other vertices. + */ + for (i = v->v_order + 1; i < g->g_size; i++) { + w = g->g_vertices[i]; + w->v_order--; + g->g_vertices[i - 1] = w; } + g->g_size--; + + free(v, M_LOCKF); +} + +static struct owner_graph * +graph_init(struct owner_graph *g) +{ + + g->g_vertices = malloc(10 * sizeof(struct owner_vertex *), + M_LOCKF, M_WAITOK); + g->g_size = 0; + g->g_space = 10; + g->g_indexbuf = malloc(g->g_space * sizeof(int), M_LOCKF, M_WAITOK); + g->g_gen = 0; + + return (g); } #ifdef LOCKF_DEBUG /* + * Print description of a lock owner + */ +static void +lf_print_owner(struct lock_owner *lo) +{ + + if (lo->lo_flags & F_REMOTE) { + printf("remote pid %d, system %d", + lo->lo_pid, lo->lo_sysid); + } else if (lo->lo_flags & F_FLOCK) { + printf("file %p", lo->lo_id); + } else { + printf("local pid %d", lo->lo_pid); + } +} + +/* * Print out a lock. */ static void -lf_print(tag, lock) - char *tag; - register struct lockf *lock; +lf_print(char *tag, struct lockf_entry *lock) { printf("%s: lock %p for ", tag, (void *)lock); - if (lock->lf_flags & F_POSIX) - printf("proc %ld", (long)((struct proc *)lock->lf_id)->p_pid); - else - printf("id %p", (void *)lock->lf_id); + lf_print_owner(lock->lf_owner); if (lock->lf_inode != (struct inode *)0) - printf(" in ino %ju on dev <%s>, %s, start %jd, end %jd", + printf(" in ino %ju on dev <%s>,", (uintmax_t)lock->lf_inode->i_number, - devtoname(lock->lf_inode->i_dev), - lock->lf_type == F_RDLCK ? "shared" : - lock->lf_type == F_WRLCK ? "exclusive" : - lock->lf_type == F_UNLCK ? "unlock" : "unknown", - (intmax_t)lock->lf_start, (intmax_t)lock->lf_end); + devtoname(lock->lf_inode->i_dev)); + printf(" %s, start %jd, end ", + lock->lf_type == F_RDLCK ? "shared" : + lock->lf_type == F_WRLCK ? "exclusive" : + lock->lf_type == F_UNLCK ? "unlock" : "unknown", + (intmax_t)lock->lf_start); + if (lock->lf_end == OFF_MAX) + printf("EOF"); else - printf(" %s, start %jd, end %jd", - lock->lf_type == F_RDLCK ? "shared" : - lock->lf_type == F_WRLCK ? "exclusive" : - lock->lf_type == F_UNLCK ? "unlock" : "unknown", - (intmax_t)lock->lf_start, (intmax_t)lock->lf_end); - if (!TAILQ_EMPTY(&lock->lf_blkhd)) - printf(" block %p\n", (void *)TAILQ_FIRST(&lock->lf_blkhd)); + printf("%jd", (intmax_t)lock->lf_end); + if (!LIST_EMPTY(&lock->lf_outedges)) + printf(" block %p\n", + (void *)LIST_FIRST(&lock->lf_outedges)->le_to); else printf("\n"); } static void -lf_printlist(tag, lock) - char *tag; - struct lockf *lock; +lf_printlist(char *tag, struct lockf_entry *lock) { - register struct lockf *lf, *blk; + struct lockf_entry *lf, *blk; + struct lockf_edge *e; if (lock->lf_inode == (struct inode *)0) return; @@ -826,32 +2296,25 @@ printf("%s: Lock list for ino %ju on dev <%s>:\n", tag, (uintmax_t)lock->lf_inode->i_number, devtoname(lock->lf_inode->i_dev)); - for (lf = lock->lf_inode->i_lockf; lf; lf = lf->lf_next) { + LIST_FOREACH(lf, &lock->lf_inode->i_lockf->ls_active, lf_link) { printf("\tlock %p for ",(void *)lf); - if (lf->lf_flags & F_POSIX) - printf("proc %ld", - (long)((struct proc *)lf->lf_id)->p_pid); - else - printf("id %p", (void *)lf->lf_id); + lf_print_owner(lock->lf_owner); printf(", %s, start %jd, end %jd", lf->lf_type == F_RDLCK ? "shared" : lf->lf_type == F_WRLCK ? "exclusive" : lf->lf_type == F_UNLCK ? "unlock" : "unknown", (intmax_t)lf->lf_start, (intmax_t)lf->lf_end); - TAILQ_FOREACH(blk, &lf->lf_blkhd, lf_block) { + LIST_FOREACH(e, &lf->lf_outedges, le_outlink) { + blk = e->le_to; printf("\n\t\tlock request %p for ", (void *)blk); - if (blk->lf_flags & F_POSIX) - printf("proc %ld", - (long)((struct proc *)blk->lf_id)->p_pid); - else - printf("id %p", (void *)blk->lf_id); + lf_print_owner(blk->lf_owner); printf(", %s, start %jd, end %jd", blk->lf_type == F_RDLCK ? "shared" : blk->lf_type == F_WRLCK ? "exclusive" : blk->lf_type == F_UNLCK ? "unlock" : "unknown", (intmax_t)blk->lf_start, (intmax_t)blk->lf_end); - if (!TAILQ_EMPTY(&blk->lf_blkhd)) + if (!LIST_EMPTY(&blk->lf_inedges)) panic("lf_printlist: bad list"); } printf("\n"); Index: sys/kern/syscalls.c =================================================================== RCS file: /home/ncvs/src/sys/kern/syscalls.c,v retrieving revision 1.181.2.5 diff -u -r1.181.2.5 syscalls.c --- sys/kern/syscalls.c 10 Oct 2006 13:47:59 -0000 1.181.2.5 +++ sys/kern/syscalls.c 28 Mar 2008 15:08:01 -0000 @@ -2,7 +2,7 @@ * System call names. * * DO NOT EDIT-- this file is automatically generated. - * $FreeBSD: src/sys/kern/syscalls.c,v 1.181.2.5 2006/10/10 13:47:59 rwatson Exp $ + * $FreeBSD$ * created from FreeBSD: src/sys/kern/syscalls.master,v 1.198.2.5 2006/10/10 13:19:47 rwatson Exp */ @@ -161,7 +161,7 @@ "#151", /* 151 = sem_lock */ "#152", /* 152 = sem_wakeup */ "#153", /* 153 = asyncdaemon */ - "#154", /* 154 = nosys */ + "nlm_syscall", /* 154 = nlm_syscall */ "nfssvc", /* 155 = nfssvc */ "old.getdirentries", /* 156 = old getdirentries */ "old.statfs", /* 157 = old statfs */ Index: sys/kern/syscalls.master =================================================================== RCS file: /home/ncvs/src/sys/kern/syscalls.master,v retrieving revision 1.198.2.5 diff -u -r1.198.2.5 syscalls.master --- sys/kern/syscalls.master 10 Oct 2006 13:19:47 -0000 1.198.2.5 +++ sys/kern/syscalls.master 28 Mar 2008 15:07:30 -0000 @@ -301,7 +301,8 @@ 151 AUE_NULL UNIMPL sem_lock (BSD/OS 2.x) 152 AUE_NULL UNIMPL sem_wakeup (BSD/OS 2.x) 153 AUE_NULL UNIMPL asyncdaemon (BSD/OS 2.x) -154 AUE_NULL UNIMPL nosys +; 154 is initialised by the NLM code, if present. +154 AUE_NULL MNOSTD { int nlm_syscall(int debug_level, int grace_period, int addr_count, char **addrs); } ; 155 is initialized by the NFS code, if present. 155 AUE_NFS_SVC MNOIMPL { int nfssvc(int flag, caddr_t argp); } 156 AUE_GETDIRENTRIES COMPAT { int getdirentries(int fd, char *buf, \ Index: sys/kern/vnode_if.src =================================================================== RCS file: /home/ncvs/src/sys/kern/vnode_if.src,v retrieving revision 1.78.2.1 diff -u -r1.78.2.1 vnode_if.src --- sys/kern/vnode_if.src 25 Feb 2008 08:55:01 -0000 1.78.2.1 +++ sys/kern/vnode_if.src 27 Mar 2008 14:55:32 -0000 @@ -447,6 +447,19 @@ }; # +#% advlockasync vp U U U +# +vop_advlockasync { + IN struct vnode *vp; + IN void *id; + IN int op; + IN struct flock *fl; + IN int flags; + IN struct task *task; + INOUT void **cookiep; +}; + +# #% reallocblks vp E E E # vop_reallocblks { Index: sys/modules/Makefile =================================================================== RCS file: /home/ncvs/src/sys/modules/Makefile,v retrieving revision 1.450.2.36 diff -u -r1.450.2.36 Makefile --- sys/modules/Makefile 15 Dec 2007 06:03:43 -0000 1.450.2.36 +++ sys/modules/Makefile 27 Mar 2008 17:23:38 -0000 @@ -136,6 +136,7 @@ ${_ixgb} \ joy \ kbdmux \ + krpc \ kue \ le \ lge \ @@ -178,6 +179,7 @@ ${_ndis} \ netgraph \ nfsclient \ + nfslockd \ nfsserver \ nge \ nmdm \ Index: sys/modules/krpc/Makefile =================================================================== RCS file: sys/modules/krpc/Makefile diff -N sys/modules/krpc/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/modules/krpc/Makefile 27 Mar 2008 14:45:46 -0000 @@ -0,0 +1,44 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../rpc ${.CURDIR}/../../xdr +KMOD= krpc +SRCS= auth_none.c \ + auth_unix.c \ + authunix_prot.c \ + clnt_dg.c \ + clnt_rc.c \ + clnt_vc.c \ + getnetconfig.c \ + inet_ntop.c \ + inet_pton.c \ + rpc_callmsg.c \ + rpc_generic.c \ + rpc_prot.c \ + rpcb_clnt.c \ + rpcb_prot.c \ + svc.c \ + svc_auth.c \ + svc_auth_unix.c \ + svc_dg.c \ + svc_generic.c \ + svc_vc.c \ + +SRCS+= xdr.c \ + xdr_array.c \ + xdr_mbuf.c \ + xdr_mem.c \ + xdr_reference.c \ + xdr_sizeof.c + +SRCS+= opt_inet6.h + +.if !defined(KERNBUILDDIR) +NFS_INET6?= 1 # 0/1 - requires INET6 to be configured in kernel + +.if ${NFS_INET6} > 0 +opt_inet6.h: + echo "#define INET6 1" > ${.TARGET} +.endif +.endif + +.include Index: sys/modules/nfslockd/Makefile =================================================================== RCS file: sys/modules/nfslockd/Makefile diff -N sys/modules/nfslockd/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/modules/nfslockd/Makefile 27 Mar 2008 14:45:46 -0000 @@ -0,0 +1,23 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../nlm ${.CURDIR}/../../rpc +KMOD= nfslockd +SRCS= vnode_if.h \ + nlm_prot_clnt.c \ + nlm_prot_impl.c \ + nlm_prot_server.c \ + nlm_prot_svc.c \ + nlm_prot_xdr.c \ + sm_inter_xdr.c +SRCS+= opt_inet6.h + +.if !defined(KERNBUILDDIR) +NFS_INET6?= 1 # 0/1 - requires INET6 to be configured in kernel + +.if ${NFS_INET6} > 0 +opt_inet6.h: + echo "#define INET6 1" > ${.TARGET} +.endif +.endif + +.include Index: sys/nfs4client/nfs4_vnops.c =================================================================== RCS file: /home/ncvs/src/sys/nfs4client/nfs4_vnops.c,v retrieving revision 1.31.2.4 diff -u -r1.31.2.4 nfs4_vnops.c --- sys/nfs4client/nfs4_vnops.c 11 Jun 2007 10:53:47 -0000 1.31.2.4 +++ sys/nfs4client/nfs4_vnops.c 27 Mar 2008 14:45:35 -0000 @@ -158,6 +158,7 @@ static vop_readlink_t nfs4_readlink; static vop_print_t nfs4_print; static vop_advlock_t nfs4_advlock; +static vop_advlockasync_t nfs4_advlockasync; /* * Global vfs data structures for nfs @@ -166,6 +167,7 @@ .vop_default = &default_vnodeops, .vop_access = nfs4_access, .vop_advlock = nfs4_advlock, + .vop_advlockasync = nfs4_advlockasync, .vop_close = nfs4_close, .vop_create = nfs4_create, .vop_fsync = nfs4_fsync, @@ -2777,6 +2779,22 @@ } /* + * NFS advisory byte-level locks. + */ +static int +nfs4_advlockasync(struct vop_advlockasync_args *ap) +{ + return (EPERM); + + if ((VFSTONFS(ap->a_vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) { + struct nfsnode *np = VTONFS(ap->a_vp); + + return (lf_advlockasync(ap, &(np->n_lockf), np->n_size)); + } + return (EOPNOTSUPP); +} + +/* * Print out the contents of an nfsnode. */ static int Index: sys/nfsclient/nfs_lock.c =================================================================== RCS file: /home/ncvs/src/sys/nfsclient/nfs_lock.c,v retrieving revision 1.40.2.2 diff -u -r1.40.2.2 nfs_lock.c --- sys/nfsclient/nfs_lock.c 14 Feb 2006 00:06:32 -0000 1.40.2.2 +++ sys/nfsclient/nfs_lock.c 27 Mar 2008 14:45:35 -0000 @@ -319,6 +319,7 @@ if (msg.lm_getlk && p->p_nlminfo->retcode == 0) { if (p->p_nlminfo->set_getlk_pid) { + fl->l_sysid = 0; /* XXX */ fl->l_pid = p->p_nlminfo->getlk_pid; } else { fl->l_type = F_UNLCK; Index: sys/nfsclient/nfs_vnops.c =================================================================== RCS file: /home/ncvs/src/sys/nfsclient/nfs_vnops.c,v retrieving revision 1.258.2.16 diff -u -r1.258.2.16 nfs_vnops.c --- sys/nfsclient/nfs_vnops.c 1 Mar 2008 11:33:22 -0000 1.258.2.16 +++ sys/nfsclient/nfs_vnops.c 27 Mar 2008 14:45:35 -0000 @@ -129,6 +129,7 @@ static vop_readlink_t nfs_readlink; static vop_print_t nfs_print; static vop_advlock_t nfs_advlock; +static vop_advlockasync_t nfs_advlockasync; /* * Global vfs data structures for nfs @@ -137,6 +138,7 @@ .vop_default = &default_vnodeops, .vop_access = nfs_access, .vop_advlock = nfs_advlock, + .vop_advlockasync = nfs_advlockasync, .vop_close = nfs_close, .vop_create = nfs_create, .vop_fsync = nfs_fsync, @@ -2908,6 +2910,27 @@ } /* + * NFS advisory byte-level locks. + */ +static int +nfs_advlockasync(struct vop_advlockasync_args *ap) +{ + int error; + + mtx_lock(&Giant); + if ((VFSTONFS(ap->a_vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) { + struct nfsnode *np = VTONFS(ap->a_vp); + + error = lf_advlockasync(ap, &(np->n_lockf), np->n_size); + goto out; + } + error = EOPNOTSUPP; +out: + mtx_unlock(&Giant); + return (error); +} + +/* * Print out the contents of an nfsnode. */ static int Index: sys/nfsserver/nfs_srvsock.c =================================================================== RCS file: /home/ncvs/src/sys/nfsserver/nfs_srvsock.c,v retrieving revision 1.94.2.3 diff -u -r1.94.2.3 nfs_srvsock.c --- sys/nfsserver/nfs_srvsock.c 2 Sep 2006 23:58:20 -0000 1.94.2.3 +++ sys/nfsserver/nfs_srvsock.c 27 Mar 2008 14:45:35 -0000 @@ -667,14 +667,17 @@ NFSD_UNLOCK(); rec = malloc(sizeof(struct nfsrv_rec), M_NFSRVDESC, waitflag == M_DONTWAIT ? M_NOWAIT : M_WAITOK); - NFSD_LOCK(); - if (!rec) { - m_freem(slp->ns_frag); - } else { + if (rec) { nfs_realign(&slp->ns_frag, 10 * NFSX_UNSIGNED); rec->nr_address = NULL; rec->nr_packet = slp->ns_frag; + NFSD_LOCK(); STAILQ_INSERT_TAIL(&slp->ns_rec, rec, nr_link); + } else { + NFSD_LOCK(); + } + if (!rec) { + m_freem(slp->ns_frag); } slp->ns_frag = NULL; } Index: sys/nlm/nlm.h =================================================================== RCS file: sys/nlm/nlm.h diff -N sys/nlm/nlm.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/nlm/nlm.h 27 Mar 2008 14:45:46 -0000 @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ + * Authors: Doug Rabson + * Developed with Red Inc: Alfred Perlstein + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NLM_NLM_H_ +#define _NLM_NLM_H_ + +#ifdef _KERNEL + +#ifdef _SYS_MALLOC_H_ +MALLOC_DECLARE(M_NLM); +#endif + +struct nlm_host; + +/* + * Copy a struct netobj. + */ +extern void nlm_copy_netobj(struct netobj *dst, struct netobj *src, + struct malloc_type *type); + +/* + * Search for an existing NLM host that matches the given name + * (typically the caller_name element of an nlm4_lock). If none is + * found, create a new host. If 'rqstp' is non-NULL, record the remote + * address of the host so that we can call it back for async + * responses. + */ +extern struct nlm_host *nlm_find_host_by_name(const char *name, + struct svc_req *rqstp); + +/* + * Search for an existing NLM host that matches the given remote + * address. If none is found, create a new host with the requested + * address and remember 'vers' as the NLM protocol version to use for + * that host. + */ +extern struct nlm_host *nlm_find_host_by_addr(const struct sockaddr *addr, + int vers); + +/* + * Return an RPC client handle that can be used to talk to the NLM + * running on the given host. + */ +extern CLIENT *nlm_host_get_rpc(struct nlm_host *host); + +/* + * Called when a host restarts. + */ +extern void nlm_sm_notify(nlm_sm_status *argp); + +/* + * Implementation for lock testing RPCs. Returns the NLM host that + * matches the RPC arguments. + */ +extern struct nlm_host *nlm_do_test(nlm4_testargs *argp, + nlm4_testres *result, struct svc_req *rqstp); + +/* + * Implementation for lock setting RPCs. Returns the NLM host that + * matches the RPC arguments. If monitor is TRUE, set up an NSM + * monitor for this host. + */ +extern struct nlm_host *nlm_do_lock(nlm4_lockargs *argp, + nlm4_res *result, struct svc_req *rqstp, bool_t monitor); + +/* + * Implementation for cancelling a pending lock request. Returns the + * NLM host that matches the RPC arguments. + */ +extern struct nlm_host *nlm_do_cancel(nlm4_cancargs *argp, + nlm4_res *result, struct svc_req *rqstp); + +/* + * Implementation for unlocking RPCs. Returns the NLM host that + * matches the RPC arguments. + */ +extern struct nlm_host *nlm_do_unlock(nlm4_unlockargs *argp, + nlm4_res *result, struct svc_req *rqstp); + +/* + * Free all locks associated with the hostname argp->name. + */ +extern void nlm_do_free_all(nlm4_notify *argp); + +/* + * Find an RPC transport that can be used to communicate with the + * userland part of lockd. + */ +extern CLIENT *nlm_user_lockd(void); + +#endif + +#endif Index: sys/nlm/nlm_prot.h =================================================================== RCS file: sys/nlm/nlm_prot.h diff -N sys/nlm/nlm_prot.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/nlm/nlm_prot.h 27 Mar 2008 14:45:46 -0000 @@ -0,0 +1,448 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ +/* $FreeBSD$ */ + +#ifndef _NLM_PROT_H_RPCGEN +#define _NLM_PROT_H_RPCGEN + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LM_MAXSTRLEN 1024 +#define MAXNAMELEN LM_MAXSTRLEN+1 + +enum nlm_stats { + nlm_granted = 0, + nlm_denied = 1, + nlm_denied_nolocks = 2, + nlm_blocked = 3, + nlm_denied_grace_period = 4, + nlm_deadlck = 5 +}; +typedef enum nlm_stats nlm_stats; + +struct nlm_holder { + bool_t exclusive; + int svid; + netobj oh; + u_int l_offset; + u_int l_len; +}; +typedef struct nlm_holder nlm_holder; + +struct nlm_testrply { + nlm_stats stat; + union { + struct nlm_holder holder; + } nlm_testrply_u; +}; +typedef struct nlm_testrply nlm_testrply; + +struct nlm_stat { + nlm_stats stat; +}; +typedef struct nlm_stat nlm_stat; + +struct nlm_res { + netobj cookie; + nlm_stat stat; +}; +typedef struct nlm_res nlm_res; + +struct nlm_testres { + netobj cookie; + nlm_testrply stat; +}; +typedef struct nlm_testres nlm_testres; + +struct nlm_lock { + char *caller_name; + netobj fh; + netobj oh; + int svid; + u_int l_offset; + u_int l_len; +}; +typedef struct nlm_lock nlm_lock; + +struct nlm_lockargs { + netobj cookie; + bool_t block; + bool_t exclusive; + struct nlm_lock alock; + bool_t reclaim; + int state; +}; +typedef struct nlm_lockargs nlm_lockargs; + +struct nlm_cancargs { + netobj cookie; + bool_t block; + bool_t exclusive; + struct nlm_lock alock; +}; +typedef struct nlm_cancargs nlm_cancargs; + +struct nlm_testargs { + netobj cookie; + bool_t exclusive; + struct nlm_lock alock; +}; +typedef struct nlm_testargs nlm_testargs; + +struct nlm_unlockargs { + netobj cookie; + struct nlm_lock alock; +}; +typedef struct nlm_unlockargs nlm_unlockargs; +/* + * The following enums are actually bit encoded for efficient + * boolean algebra.... DON'T change them..... + */ + +enum fsh_mode { + fsm_DN = 0, + fsm_DR = 1, + fsm_DW = 2, + fsm_DRW = 3 +}; +typedef enum fsh_mode fsh_mode; + +enum fsh_access { + fsa_NONE = 0, + fsa_R = 1, + fsa_W = 2, + fsa_RW = 3 +}; +typedef enum fsh_access fsh_access; + +struct nlm_share { + char *caller_name; + netobj fh; + netobj oh; + fsh_mode mode; + fsh_access access; +}; +typedef struct nlm_share nlm_share; + +struct nlm_shareargs { + netobj cookie; + nlm_share share; + bool_t reclaim; +}; +typedef struct nlm_shareargs nlm_shareargs; + +struct nlm_shareres { + netobj cookie; + nlm_stats stat; + int sequence; +}; +typedef struct nlm_shareres nlm_shareres; + +struct nlm_notify { + char *name; + long state; +}; +typedef struct nlm_notify nlm_notify; +/* definitions for NLM version 4 */ + +enum nlm4_stats { + nlm4_granted = 0, + nlm4_denied = 1, + nlm4_denied_nolocks = 2, + nlm4_blocked = 3, + nlm4_denied_grace_period = 4, + nlm4_deadlck = 5, + nlm4_rofs = 6, + nlm4_stale_fh = 7, + nlm4_fbig = 8, + nlm4_failed = 9 +}; +typedef enum nlm4_stats nlm4_stats; + +struct nlm4_stat { + nlm4_stats stat; +}; +typedef struct nlm4_stat nlm4_stat; + +struct nlm4_holder { + bool_t exclusive; + u_int32_t svid; + netobj oh; + u_int64_t l_offset; + u_int64_t l_len; +}; +typedef struct nlm4_holder nlm4_holder; + +struct nlm4_lock { + char *caller_name; + netobj fh; + netobj oh; + u_int32_t svid; + u_int64_t l_offset; + u_int64_t l_len; +}; +typedef struct nlm4_lock nlm4_lock; + +struct nlm4_share { + char *caller_name; + netobj fh; + netobj oh; + fsh_mode mode; + fsh_access access; +}; +typedef struct nlm4_share nlm4_share; + +struct nlm4_testrply { + nlm4_stats stat; + union { + struct nlm4_holder holder; + } nlm4_testrply_u; +}; +typedef struct nlm4_testrply nlm4_testrply; + +struct nlm4_testres { + netobj cookie; + nlm4_testrply stat; +}; +typedef struct nlm4_testres nlm4_testres; + +struct nlm4_testargs { + netobj cookie; + bool_t exclusive; + struct nlm4_lock alock; +}; +typedef struct nlm4_testargs nlm4_testargs; + +struct nlm4_res { + netobj cookie; + nlm4_stat stat; +}; +typedef struct nlm4_res nlm4_res; + +struct nlm4_lockargs { + netobj cookie; + bool_t block; + bool_t exclusive; + struct nlm4_lock alock; + bool_t reclaim; + int state; +}; +typedef struct nlm4_lockargs nlm4_lockargs; + +struct nlm4_cancargs { + netobj cookie; + bool_t block; + bool_t exclusive; + struct nlm4_lock alock; +}; +typedef struct nlm4_cancargs nlm4_cancargs; + +struct nlm4_unlockargs { + netobj cookie; + struct nlm4_lock alock; +}; +typedef struct nlm4_unlockargs nlm4_unlockargs; + +struct nlm4_shareargs { + netobj cookie; + nlm4_share share; + bool_t reclaim; +}; +typedef struct nlm4_shareargs nlm4_shareargs; + +struct nlm4_shareres { + netobj cookie; + nlm4_stats stat; + int sequence; +}; +typedef struct nlm4_shareres nlm4_shareres; + +struct nlm_sm_status { + char *mon_name; + int state; + char priv[16]; +}; +typedef struct nlm_sm_status nlm_sm_status; + +struct nlm4_notify { + char *name; + int32_t state; +}; +typedef struct nlm4_notify nlm4_notify; + +#define NLM_PROG ((unsigned long)(100021)) +#define NLM_SM ((unsigned long)(0)) + +#define NLM_SM_NOTIFY ((unsigned long)(1)) +extern enum clnt_stat nlm_sm_notify_0(struct nlm_sm_status *, void *, CLIENT *); +extern bool_t nlm_sm_notify_0_svc(struct nlm_sm_status *, void *, struct svc_req *); +#define NLM_VERS ((unsigned long)(1)) + +#define NLM_TEST ((unsigned long)(1)) +extern enum clnt_stat nlm_test_1(struct nlm_testargs *, nlm_testres *, CLIENT *); +extern bool_t nlm_test_1_svc(struct nlm_testargs *, nlm_testres *, struct svc_req *); +#define NLM_LOCK ((unsigned long)(2)) +extern enum clnt_stat nlm_lock_1(struct nlm_lockargs *, nlm_res *, CLIENT *); +extern bool_t nlm_lock_1_svc(struct nlm_lockargs *, nlm_res *, struct svc_req *); +#define NLM_CANCEL ((unsigned long)(3)) +extern enum clnt_stat nlm_cancel_1(struct nlm_cancargs *, nlm_res *, CLIENT *); +extern bool_t nlm_cancel_1_svc(struct nlm_cancargs *, nlm_res *, struct svc_req *); +#define NLM_UNLOCK ((unsigned long)(4)) +extern enum clnt_stat nlm_unlock_1(struct nlm_unlockargs *, nlm_res *, CLIENT *); +extern bool_t nlm_unlock_1_svc(struct nlm_unlockargs *, nlm_res *, struct svc_req *); +#define NLM_GRANTED ((unsigned long)(5)) +extern enum clnt_stat nlm_granted_1(struct nlm_testargs *, nlm_res *, CLIENT *); +extern bool_t nlm_granted_1_svc(struct nlm_testargs *, nlm_res *, struct svc_req *); +#define NLM_TEST_MSG ((unsigned long)(6)) +extern enum clnt_stat nlm_test_msg_1(struct nlm_testargs *, void *, CLIENT *); +extern bool_t nlm_test_msg_1_svc(struct nlm_testargs *, void *, struct svc_req *); +#define NLM_LOCK_MSG ((unsigned long)(7)) +extern enum clnt_stat nlm_lock_msg_1(struct nlm_lockargs *, void *, CLIENT *); +extern bool_t nlm_lock_msg_1_svc(struct nlm_lockargs *, void *, struct svc_req *); +#define NLM_CANCEL_MSG ((unsigned long)(8)) +extern enum clnt_stat nlm_cancel_msg_1(struct nlm_cancargs *, void *, CLIENT *); +extern bool_t nlm_cancel_msg_1_svc(struct nlm_cancargs *, void *, struct svc_req *); +#define NLM_UNLOCK_MSG ((unsigned long)(9)) +extern enum clnt_stat nlm_unlock_msg_1(struct nlm_unlockargs *, void *, CLIENT *); +extern bool_t nlm_unlock_msg_1_svc(struct nlm_unlockargs *, void *, struct svc_req *); +#define NLM_GRANTED_MSG ((unsigned long)(10)) +extern enum clnt_stat nlm_granted_msg_1(struct nlm_testargs *, void *, CLIENT *); +extern bool_t nlm_granted_msg_1_svc(struct nlm_testargs *, void *, struct svc_req *); +#define NLM_TEST_RES ((unsigned long)(11)) +extern enum clnt_stat nlm_test_res_1(nlm_testres *, void *, CLIENT *); +extern bool_t nlm_test_res_1_svc(nlm_testres *, void *, struct svc_req *); +#define NLM_LOCK_RES ((unsigned long)(12)) +extern enum clnt_stat nlm_lock_res_1(nlm_res *, void *, CLIENT *); +extern bool_t nlm_lock_res_1_svc(nlm_res *, void *, struct svc_req *); +#define NLM_CANCEL_RES ((unsigned long)(13)) +extern enum clnt_stat nlm_cancel_res_1(nlm_res *, void *, CLIENT *); +extern bool_t nlm_cancel_res_1_svc(nlm_res *, void *, struct svc_req *); +#define NLM_UNLOCK_RES ((unsigned long)(14)) +extern enum clnt_stat nlm_unlock_res_1(nlm_res *, void *, CLIENT *); +extern bool_t nlm_unlock_res_1_svc(nlm_res *, void *, struct svc_req *); +#define NLM_GRANTED_RES ((unsigned long)(15)) +extern enum clnt_stat nlm_granted_res_1(nlm_res *, void *, CLIENT *); +extern bool_t nlm_granted_res_1_svc(nlm_res *, void *, struct svc_req *); +extern int nlm_prog_1_freeresult(SVCXPRT *, xdrproc_t, caddr_t); +#define NLM_VERSX ((unsigned long)(3)) + +#define NLM_SHARE ((unsigned long)(20)) +extern enum clnt_stat nlm_share_3(nlm_shareargs *, nlm_shareres *, CLIENT *); +extern bool_t nlm_share_3_svc(nlm_shareargs *, nlm_shareres *, struct svc_req *); +#define NLM_UNSHARE ((unsigned long)(21)) +extern enum clnt_stat nlm_unshare_3(nlm_shareargs *, nlm_shareres *, CLIENT *); +extern bool_t nlm_unshare_3_svc(nlm_shareargs *, nlm_shareres *, struct svc_req *); +#define NLM_NM_LOCK ((unsigned long)(22)) +extern enum clnt_stat nlm_nm_lock_3(nlm_lockargs *, nlm_res *, CLIENT *); +extern bool_t nlm_nm_lock_3_svc(nlm_lockargs *, nlm_res *, struct svc_req *); +#define NLM_FREE_ALL ((unsigned long)(23)) +extern enum clnt_stat nlm_free_all_3(nlm_notify *, void *, CLIENT *); +extern bool_t nlm_free_all_3_svc(nlm_notify *, void *, struct svc_req *); +extern int nlm_prog_3_freeresult(SVCXPRT *, xdrproc_t, caddr_t); +#define NLM_VERS4 ((unsigned long)(4)) + +#define NLM4_TEST ((unsigned long)(1)) +extern enum clnt_stat nlm4_test_4(nlm4_testargs *, nlm4_testres *, CLIENT *); +extern bool_t nlm4_test_4_svc(nlm4_testargs *, nlm4_testres *, struct svc_req *); +#define NLM4_LOCK ((unsigned long)(2)) +extern enum clnt_stat nlm4_lock_4(nlm4_lockargs *, nlm4_res *, CLIENT *); +extern bool_t nlm4_lock_4_svc(nlm4_lockargs *, nlm4_res *, struct svc_req *); +#define NLM4_CANCEL ((unsigned long)(3)) +extern enum clnt_stat nlm4_cancel_4(nlm4_cancargs *, nlm4_res *, CLIENT *); +extern bool_t nlm4_cancel_4_svc(nlm4_cancargs *, nlm4_res *, struct svc_req *); +#define NLM4_UNLOCK ((unsigned long)(4)) +extern enum clnt_stat nlm4_unlock_4(nlm4_unlockargs *, nlm4_res *, CLIENT *); +extern bool_t nlm4_unlock_4_svc(nlm4_unlockargs *, nlm4_res *, struct svc_req *); +#define NLM4_GRANTED ((unsigned long)(5)) +extern enum clnt_stat nlm4_granted_4(nlm4_testargs *, nlm4_res *, CLIENT *); +extern bool_t nlm4_granted_4_svc(nlm4_testargs *, nlm4_res *, struct svc_req *); +#define NLM4_TEST_MSG ((unsigned long)(6)) +extern enum clnt_stat nlm4_test_msg_4(nlm4_testargs *, void *, CLIENT *); +extern bool_t nlm4_test_msg_4_svc(nlm4_testargs *, void *, struct svc_req *); +#define NLM4_LOCK_MSG ((unsigned long)(7)) +extern enum clnt_stat nlm4_lock_msg_4(nlm4_lockargs *, void *, CLIENT *); +extern bool_t nlm4_lock_msg_4_svc(nlm4_lockargs *, void *, struct svc_req *); +#define NLM4_CANCEL_MSG ((unsigned long)(8)) +extern enum clnt_stat nlm4_cancel_msg_4(nlm4_cancargs *, void *, CLIENT *); +extern bool_t nlm4_cancel_msg_4_svc(nlm4_cancargs *, void *, struct svc_req *); +#define NLM4_UNLOCK_MSG ((unsigned long)(9)) +extern enum clnt_stat nlm4_unlock_msg_4(nlm4_unlockargs *, void *, CLIENT *); +extern bool_t nlm4_unlock_msg_4_svc(nlm4_unlockargs *, void *, struct svc_req *); +#define NLM4_GRANTED_MSG ((unsigned long)(10)) +extern enum clnt_stat nlm4_granted_msg_4(nlm4_testargs *, void *, CLIENT *); +extern bool_t nlm4_granted_msg_4_svc(nlm4_testargs *, void *, struct svc_req *); +#define NLM4_TEST_RES ((unsigned long)(11)) +extern enum clnt_stat nlm4_test_res_4(nlm4_testres *, void *, CLIENT *); +extern bool_t nlm4_test_res_4_svc(nlm4_testres *, void *, struct svc_req *); +#define NLM4_LOCK_RES ((unsigned long)(12)) +extern enum clnt_stat nlm4_lock_res_4(nlm4_res *, void *, CLIENT *); +extern bool_t nlm4_lock_res_4_svc(nlm4_res *, void *, struct svc_req *); +#define NLM4_CANCEL_RES ((unsigned long)(13)) +extern enum clnt_stat nlm4_cancel_res_4(nlm4_res *, void *, CLIENT *); +extern bool_t nlm4_cancel_res_4_svc(nlm4_res *, void *, struct svc_req *); +#define NLM4_UNLOCK_RES ((unsigned long)(14)) +extern enum clnt_stat nlm4_unlock_res_4(nlm4_res *, void *, CLIENT *); +extern bool_t nlm4_unlock_res_4_svc(nlm4_res *, void *, struct svc_req *); +#define NLM4_GRANTED_RES ((unsigned long)(15)) +extern enum clnt_stat nlm4_granted_res_4(nlm4_res *, void *, CLIENT *); +extern bool_t nlm4_granted_res_4_svc(nlm4_res *, void *, struct svc_req *); +#define NLM4_SHARE ((unsigned long)(20)) +extern enum clnt_stat nlm4_share_4(nlm4_shareargs *, nlm4_shareres *, CLIENT *); +extern bool_t nlm4_share_4_svc(nlm4_shareargs *, nlm4_shareres *, struct svc_req *); +#define NLM4_UNSHARE ((unsigned long)(21)) +extern enum clnt_stat nlm4_unshare_4(nlm4_shareargs *, nlm4_shareres *, CLIENT *); +extern bool_t nlm4_unshare_4_svc(nlm4_shareargs *, nlm4_shareres *, struct svc_req *); +#define NLM4_NM_LOCK ((unsigned long)(22)) +extern enum clnt_stat nlm4_nm_lock_4(nlm4_lockargs *, nlm4_res *, CLIENT *); +extern bool_t nlm4_nm_lock_4_svc(nlm4_lockargs *, nlm4_res *, struct svc_req *); +#define NLM4_FREE_ALL ((unsigned long)(23)) +extern enum clnt_stat nlm4_free_all_4(nlm4_notify *, void *, CLIENT *); +extern bool_t nlm4_free_all_4_svc(nlm4_notify *, void *, struct svc_req *); +extern int nlm_prog_4_freeresult(SVCXPRT *, xdrproc_t, caddr_t); + +/* the xdr functions */ +extern bool_t xdr_nlm_stats(XDR *, nlm_stats*); +extern bool_t xdr_nlm_holder(XDR *, nlm_holder*); +extern bool_t xdr_nlm_testrply(XDR *, nlm_testrply*); +extern bool_t xdr_nlm_stat(XDR *, nlm_stat*); +extern bool_t xdr_nlm_res(XDR *, nlm_res*); +extern bool_t xdr_nlm_testres(XDR *, nlm_testres*); +extern bool_t xdr_nlm_lock(XDR *, nlm_lock*); +extern bool_t xdr_nlm_lockargs(XDR *, nlm_lockargs*); +extern bool_t xdr_nlm_cancargs(XDR *, nlm_cancargs*); +extern bool_t xdr_nlm_testargs(XDR *, nlm_testargs*); +extern bool_t xdr_nlm_unlockargs(XDR *, nlm_unlockargs*); +extern bool_t xdr_fsh_mode(XDR *, fsh_mode*); +extern bool_t xdr_fsh_access(XDR *, fsh_access*); +extern bool_t xdr_nlm_share(XDR *, nlm_share*); +extern bool_t xdr_nlm_shareargs(XDR *, nlm_shareargs*); +extern bool_t xdr_nlm_shareres(XDR *, nlm_shareres*); +extern bool_t xdr_nlm_notify(XDR *, nlm_notify*); +extern bool_t xdr_nlm4_stats(XDR