[v3] fcntl-linux.h: add new definitions for file-private lock cmd values

Message ID 1397594490-19295-1-git-send-email-jlayton@redhat.com
State Superseded

Commit Message

Jeff Layton April 15, 2014, 8:41 p.m. UTC
  File-private locks have been merged into the Linux kernel for v3.15.
Add the appropriate command-value definitions and an update to the
manual that describes their usage.


2014-04-15  Jeff Layton  <jlayton@redhat.com>

	* manual/llio.texi: add section about file-private locks

	* sysdeps/unix/sysv/linux/bits/fcntl-linux.h:
	  (F_GETLKP, F_SETLKP, F_SETLKPW): New macros.
 manual/examples/fplocks.c                  |  75 ++++++++++
 manual/llio.texi                           | 219 ++++++++++++++++++++++++++++-
 sysdeps/unix/sysv/linux/bits/fcntl-linux.h |  16 +++
 3 files changed, 308 insertions(+), 2 deletions(-)
 create mode 100644 manual/examples/fplocks.c


diff --git a/manual/examples/fplocks.c b/manual/examples/fplocks.c
new file mode 100644
index 000000000000..a603e9ff6a1a
--- /dev/null
+++ b/manual/examples/fplocks.c
@@ -0,0 +1,75 @@ 
+/* File-Private Locks Usage Example
+   Copyright (C) 1991-2014 Free Software Foundation, Inc.
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   GNU General Public License for more details.
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, if not, see <http://www.gnu.org/licenses/>.
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#define FILENAME	"/tmp/foo"
+#define NUM_THREADS	3
+#define ITERATIONS	5
+void *
+thread_start(void *arg)
+	int i, fd, len;
+	long tid = (long)arg;
+	char buf[256];
+	struct flock lck = {
+		.l_whence	= SEEK_SET,
+		.l_start	= 0,
+		.l_len		= 1,
+	};
+	fd = open("/tmp/foo", O_RDWR|O_CREAT, 0666);
+	for (i = 0; i < ITERATIONS; i++) {
+		lck.l_type = F_WRLCK;
+		fcntl(fd, F_SETLKPW, &lck);
+		len = sprintf(buf, "%d: tid=%ld fd=%d\n", i, tid, fd);
+		lseek(fd, 0, SEEK_END);
+		write(fd, buf, len);
+		fsync(fd);
+		lck.l_type = F_UNLCK;
+		fcntl(fd, F_SETLKP, &lck);
+		usleep(1);
+	}
+	pthread_exit(NULL);
+main(int argc, char **argv)
+	long i;
+	pthread_t	threads[NUM_THREADS];
+	truncate(FILENAME, 0);
+	for (i = 0; i < NUM_THREADS; i++)
+		pthread_create(&threads[i], NULL, thread_start, (void *)i);
+	pthread_exit(NULL);
+	return 0;
diff --git a/manual/llio.texi b/manual/llio.texi
index 6f8adfc607d7..e6566171cc3b 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -57,6 +57,9 @@  directly.)
                                          flags associated with open files.
 * File Locks::                          Fcntl commands for implementing
                                          file locking.
+* File-private Locks::                  Fcntl commands for implementing
+                                         file-private locking.
+* File-Private Locks Example::		An example of file-private lock usage
 * Interrupt Input::                     Getting an asynchronous signal when
                                          input arrives.
 * IOCTLs::                              Generic I/O Control operations.
@@ -2890,7 +2893,7 @@  Get flags associated with the open file.  @xref{File Status Flags}.
 Set flags associated with the open file.  @xref{File Status Flags}.
 @item F_GETLK
-Get a file lock.  @xref{File Locks}.
+Test a file lock.  @xref{File Locks}.
 @item F_SETLK
 Set or clear a file lock.  @xref{File Locks}.
@@ -2898,6 +2901,15 @@  Set or clear a file lock.  @xref{File Locks}.
 @item F_SETLKW
 Like @code{F_SETLK}, but wait for completion.  @xref{File Locks}.
+@item F_GETLKP
+Test a file-private lock.  @xref{File Locks}. Specific to Linux.
+@item F_SETLKP
+Set or clear a file lock.  @xref{File Locks}. Specific to Linux.
+@item F_SETLKPW
+Like @code{F_SETLKP}, but wait for completion.  @xref{File Locks}. Specific to Linux.
 @item F_GETOWN
 Get process or process group ID to receive @code{SIGIO} signals.
 @xref{Interrupt Input}.
@@ -3576,6 +3588,10 @@  set_nonblock_flag (int desc, int value)
 @cindex file locks
 @cindex record locking
+This section describes record locks that are associated with the process.
+There is also a different type of record lock that is associated with the
+open file table entry instead of the process.  @xref{File-private Locks}.
 The remaining @code{fcntl} commands are used to support @dfn{record
 locking}, which permits multiple cooperating programs to prevent each
 other from simultaneously accessing parts of a file in error-prone
@@ -3641,7 +3657,9 @@  the file.
 @item pid_t l_pid
 This field is the process ID (@pxref{Process Creation Concepts}) of the
 process holding the lock.  It is filled in by calling @code{fcntl} with
-the @code{F_GETLK} command, but is ignored when making a lock.
+the @code{F_GETLK} command, but is ignored when making a lock.  If the
+conflicting lock is a File-private lock (@pxref{File-private Locks}),
+then this field will be set to @math{-1}.
 @end table
 @end deftp
@@ -3817,6 +3835,203 @@  Remember that file locks are only a @emph{voluntary} protocol for
 controlling access to a file.  There is still potential for access to
 the file by programs that don't use the lock protocol.
+@node File-private Locks
+@section File-private Locks
+In contrast to process-associated record locks (@pxref{File Locks}),
+file-private locks are associated with an open file table entry rather
+than a process.  File-private locks set on an open file descriptor will never
+conflict with existing file-private locks set on that file descriptor.
+File-private locks are also inherited by child processes across
+@code{fork}, or @code{clone} with @code{CLONE_FILES} set
+(@pxref{Creating a Process}), along with the file descriptor.
+Using @code{dup} (@pxref{Duplicating Descriptors}) to copy a file
+descriptor does not give you a new instance of the open file, but
+instead copies a reference to an existing open file.  Thus, file-private
+locks set on a file descriptor cloned by @code{dup} will never conflict
+with file-private locks set on the original descriptor.
+File-private locks always conflict with process-associated record locks,
+even if acquired by the same process or on the same open file descriptor.
+File-private locks use the same @code{struct flock} as classic POSIX
+locks as an argument (@pxref{File Locks}) and the macros for the
+cmd values are also declared in the header file @file{fcntl.h}. To
+use them, the macro @code{_GNU_SOURCE} must be defined prior to
+including @file{fcntl.h}.
+In contrast to traditional record locks, any @code{struct flock} used as
+an argument to file-private lock commands must have the @code{l_pid}
+value set to @math{0}. Also, when returning information about a
+file-private lock in a @code{F_GETLK} or @code{F_GETLKP} request, the
+@code{l_pid} field in @code{struct flock} will be set to @math{-1} to
+indicate that a lock is not associated with a process.
+When the same struct flock is reused as an argument to a @code{F_SETLKP}
+or @code{F_SETLKPW} request after being used for an @code{F_GETLKP}
+request, it is necessary to inspect and reset the @code{l_pid} field
+to @math{0}.
+@pindex fcntl.h.
+@deftypevr Macro int F_GETLKP
+This macro is used as the @var{command} argument to @code{fcntl}, to
+specify that it should get information about a lock.  This command
+requires a third argument of type @w{@code{struct flock *}} to be passed
+to @code{fcntl}, so that the form of the call is:
+fcntl (@var{filedes}, F_GETLKP, @var{lockp})
+@end smallexample
+If there is a lock already in place that would block the lock described
+by the @var{lockp} argument, information about that lock overwrites
+@code{*@var{lockp}}.  Existing locks are not reported if they are
+compatible with making a new lock as specified.  Thus, you should
+specify a lock type of @code{F_WRLCK} if you want to find out about both
+read and write locks, or @code{F_RDLCK} if you want to find out about
+write locks only.
+There might be more than one lock affecting the region specified by the
+@var{lockp} argument, but @code{fcntl} only returns information about
+one of them. Which lock is returned in this situation is undefined.
+The @code{l_whence} member of the @var{lockp} structure is set to
+@code{SEEK_SET} and the @code{l_start} and @code{l_len} fields set to identify
+the locked region.
+If no lock applies, the only change to the @var{lockp} structure is to
+update the @code{l_type} to a value of @code{F_UNLCK}.
+The normal return value from @code{fcntl} with this command is an
+unspecified value other than @math{-1}, which is reserved to indicate an
+error.  The following @code{errno} error conditions are defined for
+this command:
+@table @code
+@item EBADF
+The @var{filedes} argument is invalid.
+@item EINVAL
+Either the @var{lockp} argument doesn't specify valid lock information,
+or the file associated with @var{filedes} doesn't support locks.
+@end table
+@end deftypevr
+@comment fcntl.h
+@comment POSIX.1
+@deftypevr Macro int F_SETLKP
+This macro is used as the @var{command} argument to @code{fcntl}, to
+specify that it should set or clear a lock.  This command requires a
+third argument of type @w{@code{struct flock *}} to be passed to
+@code{fcntl}, so that the form of the call is:
+fcntl (@var{filedes}, F_SETLKP, @var{lockp})
+@end smallexample
+If the opened file already has a lock on any part of the
+region, the old lock on that part is replaced with the new lock.  You
+can remove a lock by specifying a lock type of @code{F_UNLCK}.
+If the lock cannot be set, @code{fcntl} returns immediately with a value
+of @math{-1}.  This function does not wait for other tasks
+to release locks.  If @code{fcntl} succeeds, it returns a value other
+than @math{-1}.
+The following @code{errno} error conditions are defined for this
+@table @code
+@item EAGAIN
+@itemx EACCES
+The lock cannot be set because it is blocked by an existing lock on the
+file.  Some systems use @code{EAGAIN} in this case, and other systems
+use @code{EACCES}; your program should treat them alike, after
+@code{F_SETLKP}.  (@gnulinuxhurdsystems{} always use @code{EAGAIN}.)
+@item EBADF
+Either: the @var{filedes} argument is invalid; you requested a read lock
+but the @var{filedes} is not open for read access; or, you requested a
+write lock but the @var{filedes} is not open for write access.
+@item EINVAL
+Either the @var{lockp} argument doesn't specify valid lock information,
+or the file associated with @var{filedes} doesn't support locks.
+@item ENOLCK
+The system has run out of file lock resources; there are already too
+many file locks in place.
+Well-designed file systems never report this error, because they have no
+limitation on the number of locks.  However, you must still take account
+of the possibility of this error, as it could result from network access
+to a file system on another machine.
+@end table
+@end deftypevr
+@comment fcntl.h
+@comment POSIX.1
+@deftypevr Macro int F_SETLKPW
+This macro is used as the @var{command} argument to @code{fcntl}, to
+specify that it should set or clear a lock.  It is just like the
+@code{F_SETLKP} command, but causes the process to wait until the request
+can be completed.
+This command requires a third argument of type @code{struct flock *}, as
+for the @code{F_SETLKP} command.
+The @code{fcntl} return values and errors are the same as for the
+@code{F_SETLKP} command, but these additional @code{errno} error conditions
+are defined for this command:
+@table @code
+@item EINTR
+The function was interrupted by a signal while it was waiting.
+@xref{Interrupted Primitives}.
+@end table
+@end deftypevr
+File-private locks are useful in the same sorts of situations as classic
+record locks.  They can also be used to synchronize file access between
+threads within the same process by giving each thread its own open file
+Because they are only released automatically when the last reference to
+an open file is destroyed, file-private locks allow more assurance that
+the locks will not be released due to a library routine opening and
+closing a file without the application being aware.
+As with classic record locks, file-private locks are also voluntary.
+@node File-Private Locks Example
+@section File-Private Locks Example
+Here is an example of using file-private locks in a threaded program. If
+this program used process-associated record locks, then then it would be
+subject to data corruption due since multiple threads are running within
+the same process. Proper error handling has been omitted in the following
+program for brevity.
+@include fplocks.c.texi
+@end smallexample
+This example spawns three threads and has each do five iterations of
+appending to a file. Access to that file is serialized via file-private
+locks. If we compile and run the above program, we end up with /tmp/foo
+that has 15 lines in it.
+If we, however, were to replace the @code{F_SETLKP} and @code{F_SETLKPW}
+commands with their process-associated lock equivalents, the locking
+essentially becomes a noop since it is all done within the context of
+the same process. That leads to data corruption (typically manifested
+as missing lines) as some threads race in and overwrite the data from
 @node Interrupt Input
 @section Interrupt-Driven Input
diff --git a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
index 915eb3ede560..e663117e348d 100644
--- a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
+++ b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
@@ -117,6 +117,22 @@ 
 # define F_SETLKW64	14	/* Set record locking info (blocking).	*/
+/* fd "private" POSIX locks.
+   Usually POSIX locks held by a process are released on *any* close and are
+   not inherited across a fork.
+   These cmd values will set locks that conflict with normal POSIX locks, but
+   are "owned" by the opened file, not the process.  This means that they are
+   inherited across fork like BSD (flock) locks, and they are only released
+   automatically when the last reference to the the open file against which
+   they were acquired is put. */
+#if __USE_GNU
+# define F_GETLKP	36
+# define F_SETLKP	37
+# define F_SETLKPW	38
 #ifdef __USE_LARGEFILE64