Re: [glibc PATCH v2] fcntl-linux.h: add new definitions and manual updates for open file description locks

From: Michael Kerrisk (man-pages)
Date: Thu Apr 24 2014 - 09:37:03 EST


On 04/24/2014 02:39 PM, Jeff Layton wrote:
> On Thu, 24 Apr 2014 14:31:43 +0200
> "Michael Kerrisk (man-pages)" <mtk.manpages@xxxxxxxxx> wrote:
>
>> Jeff,
>>
>> Did you receive my mail with comments on the previous patch?
>> (I got no reply.) It looks like some of those comments that needed to
>> be addressed were not.
>>
>> Cheers,
>>
>> Michael
>>
>
> I did get the mail and thought I had addressed them all. The only thing
> I didn't change based on your comments was the one about the "return 0"
> in main() being unnecessary in the example program. That's not true
> since main is an int return function. I suppose we could turn it into a
> void return, but does it really matter here?

Ahhh -- okay. I see now that there are still some similar problems
in this version that I missed noting in the first version.

Cheers,

Michael

>> On 04/24/2014 02:15 PM, Jeff Layton wrote:
>>> Open file description 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.
>>>
>>> ChangeLog:
>>>
>>> 2014-04-24 Jeff Layton <jlayton@xxxxxxxxxx>
>>>
>>> [BZ#16839]
>>> * manual/llio.texi: add section about open file description locks
>>>
>>> * sysdeps/unix/sysv/linux/bits/fcntl-linux.h:
>>> (F_OFD_GETLK, F_OFD_SETLK, F_OFD_SETLKW): New macros.
>>> ---
>>> manual/examples/ofdlocks.c | 77 +++++++++
>>> manual/llio.texi | 241 ++++++++++++++++++++++++++++-
>>> sysdeps/unix/sysv/linux/bits/fcntl-linux.h | 17 ++
>>> 3 files changed, 332 insertions(+), 3 deletions(-)
>>> create mode 100644 manual/examples/ofdlocks.c
>>>
>>> diff --git a/manual/examples/ofdlocks.c b/manual/examples/ofdlocks.c
>>> new file mode 100644
>>> index 000000000000..85e193cdabe6
>>> --- /dev/null
>>> +++ b/manual/examples/ofdlocks.c
>>> @@ -0,0 +1,77 @@
>>> +/* Open File Description 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
>>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + 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, 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_OFD_SETLKW, &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_OFD_SETLK, &lck);
>>> +
>>> + /* sleep to ensure lock is yielded to another thread */
>>> + usleep (1);
>>> + }
>>> + pthread_exit (NULL);
>>> +}
>>> +
>>> +int
>>> +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..764f67d469cc 100644
>>> --- a/manual/llio.texi
>>> +++ b/manual/llio.texi
>>> @@ -57,6 +57,10 @@ directly.)
>>> flags associated with open files.
>>> * File Locks:: Fcntl commands for implementing
>>> file locking.
>>> +* Open File Description Locks:: Fcntl commands for implementing
>>> + open file description locking.
>>> +* Open File Description Locks Example:: An example of open file description lock
>>> + usage
>>> * Interrupt Input:: Getting an asynchronous signal when
>>> input arrives.
>>> * IOCTLs:: Generic I/O Control operations.
>>> @@ -2890,7 +2894,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 +2902,18 @@ 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_OFD_GETLK
>>> +Test a open file description lock. @xref{Open File Description Locks}.
>>> +Specific to Linux.
>>> +
>>> +@item F_OFD_SETLK
>>> +Set or clear a file lock. @xref{Open File Description Locks}.
>>> +Specific to Linux.
>>> +
>>> +@item F_OFD_SETLKW
>>> +Like @code{F_OFD_SETLK}, but wait for completion.
>>> +@xref{Open File Description Locks}. Specific to Linux.
>>> +
>>> @item F_GETOWN
>>> Get process or process group ID to receive @code{SIGIO} signals.
>>> @xref{Interrupt Input}.
>>> @@ -3576,6 +3592,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 description instead of the process. @xref{Open File Description 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 +3661,10 @@ 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 an open file description lock
>>> +(@pxref{Open File Description Locks}), then this field will be set to
>>> +@math{-1}.
>>> @end table
>>> @end deftp
>>>
>>> @@ -3813,10 +3836,222 @@ that part of the file for writing.
>>>
>>> @c ??? This section could use an example program.
>>>
>>> -Remember that file locks are only a @emph{voluntary} protocol for
>>> +Remember that file locks are only an @emph{advisory} 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 Open File Description Locks
>>> +@section Open File Description Locks
>>> +
>>> +In contrast to process-associated record locks (@pxref{File Locks}),
>>> +open file description record locks are associated with an open file
>>> +description rather than a process.
>>> +
>>> +Using @code{fcntl} to apply a open file description lock on a region that
>>> +already has an existing open file description lock that was created via the
>>> +same file descriptor will never cause a lock conflict.
>>> +
>>> +Open file description 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.
>>> +
>>> +It is important to distinguish between the file @emph{description} (an
>>> +instance of an open file, usually created by a call to @code{open}) and
>>> +a file @emph{descriptor}, which is a numeric value that refers to the
>>> +former. The locks described here are associated with the open file
>>> +@emph{description} and not the open file @emph{descriptor}.
>>> +
>>> +Using @code{dup} (@pxref{Duplicating Descriptors}) to copy a file
>>> +descriptor does not give you a new file description, but rather copies a
>>> +reference to an existing open file description and assigns it to a new
>>> +file descriptor. Thus, open file description locks set on a file
>>> +descriptor cloned by @code{dup} will never conflict with open file
>>> +description locks set on the original descriptor since they refer to the
>>> +same open file description. Depending on the range and type of lock
>>> +involved, the original lock may be modified by a @code{F_OFD_SETLK} or
>>> +@code{F_OFD_SETLKW} command in this situation however.
>>> +
>>> +Open file description locks always conflict with process-associated locks,
>>> +even if acquired by the same process or on the same open file
>>> +descriptor.
>>> +
>>> +Open file description locks use the same @code{struct flock} as
>>> +process-associated locks as an argument (@pxref{File Locks}) and the
>>> +macros for the @code{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 any header file.
>>> +
>>> +In contrast to process-associated locks, any @code{struct flock} used as
>>> +an argument to open file description lock commands must have the @code{l_pid}
>>> +value set to @math{0}. Also, when returning information about an
>>> +open file description lock in a @code{F_GETLK} or @code{F_OFD_GETLK} 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 @code{struct flock} is reused as an argument to a
>>> +@code{F_OFD_SETLK} or @code{F_OFD_SETLKW} request after being used for an
>>> +@code{F_OFD_GETLK} request, it is necessary to inspect and reset the
>>> +@code{l_pid} field to @math{0}.
>>> +
>>> +@pindex fcntl.h.
>>> +
>>> +@deftypevr Macro int F_OFD_GETLK
>>> +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:
>>> +
>>> +@smallexample
>>> +fcntl (@var{filedes}, F_OFD_GETLK, @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 is written to
>>> +@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 are set to
>>> +@code{SEEK_SET} and the @code{l_start} and @code{l_len} fields set to identify
>>> +the locked region.
>>> +
>>> +If no conflicting lock exists, the only change to the @var{lockp} structure
>>> +is to update the @code{l_type} field to the value @code{F_UNLCK}.
>>> +
>>> +The normal return value from @code{fcntl} with this command is either @math{0}
>>> +on success or @math{-1}, which indicates 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,
>>> +the operating system kernel doesn't support open file description locks, 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_OFD_SETLK
>>> +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:
>>> +
>>> +@smallexample
>>> +fcntl (@var{filedes}, F_OFD_SETLK, @var{lockp})
>>> +@end smallexample
>>> +
>>> +If the open 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 @math{0}.
>>> +
>>> +The following @code{errno} error conditions are defined for this
>>> +command:
>>> +
>>> +@table @code
>>> +@item EAGAIN
>>> +The lock cannot be set because it is blocked by an existing lock on the
>>> +file.
>>> +
>>> +@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,
>>> +the operating system kernel doesn't support open file description locks, 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_OFD_SETLKW
>>> +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_OFD_SETLK} 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_OFD_SETLK} command.
>>> +
>>> +The @code{fcntl} return values and errors are the same as for the
>>> +@code{F_OFD_SETLK} 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
>>> +
>>> +Open file description locks are useful in the same sorts of situations as
>>> +process-associated locks. They can also be used to synchronize file
>>> +access between threads within the same process by having each thread perform
>>> +its own @code{open} of the file, to obtain its own open file description.
>>> +
>>> +Because open file description locks are automatically freed only upon
>>> +closing the last file descriptor that refers to the open file
>>> +description, this locking mechanism avoids the possibility that locks
>>> +are inadvertantly released due to a library routine opening and closing
>>> +a file without the application being aware.
>>> +
>>> +As with process-associated locks, open file description locks are advisory.
>>> +
>>> +@node Open File Description Locks Example
>>> +@section Open File Description Locks Example
>>> +
>>> +Here is an example of using open file description locks in a threaded
>>> +program. If this program used process-associated locks, then it would be
>>> +subject to data corruption because process-associated locks are shared
>>> +by the threads inside a process, and thus cannot be used by one thread
>>> +to lock out another thread in the same process.
>>> +
>>> +Proper error handling has been omitted in the following program for
>>> +brevity.
>>> +
>>> +@smallexample
>>> +@include ofdlocks.c.texi
>>> +@end smallexample
>>> +
>>> +This example creates three threads each of which loops five times,
>>> +appending to the file. Access to the file is serialized via open file
>>> +description locks. If we compile and run the above program, we'll end up
>>> +with /tmp/foo that has 15 lines in it.
>>> +
>>> +If we, however, were to replace the @code{F_OFD_SETLK} and
>>> +@code{F_OFD_SETLKW} 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 written by others.
>>> +
>>> @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..455389cd2c2a 100644
>>> --- a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
>>> +++ b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h
>>> @@ -117,6 +117,23 @@
>>> # define F_SETLKW64 14 /* Set record locking info (blocking). */
>>> #endif
>>>
>>> +/* open file description locks.
>>> +
>>> + Usually record 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 process-associated record
>>> + locks, but are "owned" by the opened file description, not the process.
>>> + This means that they are inherited across fork or clone with CLONE_FILES
>>> + like BSD (flock) locks, and they are only released automatically when the
>>> + last reference to the the file description against which they were acquired
>>> + is put. */
>>> +#if __USE_GNU
>>> +# define F_OFD_GETLK 36
>>> +# define F_OFD_SETLK 37
>>> +# define F_OFD_SETLKW 38
>>> +#endif
>>> +
>>> #ifdef __USE_LARGEFILE64
>>> # define O_LARGEFILE __O_LARGEFILE
>>> #endif
>>>
>>
>>
>
>


--
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/