An fl_flock lock is always associated with a file object and is thus maintained by a particular process (or clone processes sharing the same opened file). When a lock is requested and granted, the kernel replaces any other lock that the process is holding on the same file object.
This happens only when a process wants to change an already owned read lock into a write one, or vice versa. Moreover, when a file object is being freed by the fput( ) function, all fl_flock locks that refer to the file object are destroyed. However, there could be other fl_flock read locks set by other processes for the same file (inode), and they still remain active.
The flock( ) system call acts on two parameters: the fd file descriptor of the file to be acted upon and a cmd parameter that specifies the lock operation. A cmd parameter of lock_sh requires a shared lock for reading, lock_ex requires an exclusive lock for writing, and lock_un releases the lock. If the lock_nb value is ORed to the lock_sh or lock_ex operation, the system call does not block; in other words, if the lock cannot be immediately obtained, the system call returns an error code. Note that it is not possible to specify a region inside the file—the lock always applies to the whole file.
When the sys_flock( ) service routine is invoked, it performs the following steps:
1. Checks whether fd is a valid file descriptor; if not, returns an error code. Gets the address of the corresponding file object.
2. If the process has to acquire an advisory lock, checks that the process has both read and write permission on the open file; if not, returns an error code.
3. Invokes flock_lock_file( ), passing as parameters the file object pointer filp, the type type of lock operation required, and a flag wait. This last parameter is set if the system call should block (lock_nb clear) and cleared otherwise (look_nb set). This function performs, in turn, the following actions:
a. If the lock must be acquired, gets a new file_lock object and fills it with the appropriate lock operation.
b. Searches the list that filp->f_dentry->d_inode->i_flock points to. If an fl_flock lock for the same file object is found and an unlock operation is required, removes the file_lock element from the inode list and the global list, wakes up all processes sleeping in the lock's wait queue, frees the file_lock structure, and returns.
c. Otherwise, searches the inode list again to verify that no existing fl_flock lock conflicts with the requested one. There must be no fl_flock write lock in the inode list, and moreover, there must be no fl_flock lock at all if the processing is requesting a write lock. However, a process may want to change the type of lock it already owns; this is done by issuing a second flock( ) system call. Therefore, the kernel always allows the process to change locks that refer to the same file object. If a conflicting lock is found and the lock_nb flag was specified, the function returns an error code;
otherwise, it inserts the current process in the circular list of blocked processes and suspends it.
d. If no incompatibility exists, inserts the file_lock structure into the global lock list and the inode list, and then returns 0 (success).
4. Returns the return code of flock_lock_file( ). 12.7.4 FL_POSIX Locks
An fl_posix lock is always associated with a process and with an inode; the lock is automatically released either when the process dies or when a file descriptor is closed (even if the process opened the same file twice or duplicated a file descriptor). Moreover, fl_posix locks are never inherited by the child across a fork( ).
When used to lock files, the fcntl( ) system call acts on three parameters: the fd file descriptor of the file to be acted upon, a cmd parameter that specifies the lock operation, and an fl pointer to a flock data structure. Version 2.4 of Linux also defines a flock64 structure, which uses 64-bit fields for the file offset and length fields. In the following, we focus on the flock data structure, but the description is valid for flock64 too.
Locks of type fl_posix are able to protect an arbitrary file region, even a single byte. The region is specified by three fields of the flock structure. l_start is the initial offset of the region and is relative to the beginning of the file (if field l_whence is set to seek_set), to the current file pointer (if l_whence is set to seek_cur), or to the end of the file (if l_whence is set to seek_end). The l_len field specifies the length of the file region (or 0, which means that the region includes all potential writes past the current end of the file).
The sys_fcntl( ) service routine behaves differently, depending on the value of the flag set in the cmd parameter:
Determines whether the lock described by the flock structure conflicts with some fl_posix lock already obtained by another process. In this case, the flock structure is overwritten with the information about the existing lock.
Sets the lock described by the flock structure. If the lock cannot be acquired, the system call returns an error code.
Sets the lock described by the flock structure. If the lock cannot be acquired, the system call blocks; that is, the calling process is put to sleep.
F_GETLK64, F_SETLK64, F_SETLKW64
Identical to the previous ones, but the flock64 data structure is used rather than flock.
When sys_fcntl( ) acquires a lock, it performs the following:
1. Reads the flock structure from user space.
3. Checks whether the lock should be a mandatory one and the file has a shared memory mapping (see Chapter 15). In this case, refuses to create the lock and returns the -eagain error code; the file is already being accessed by another process.
4. Initializes a new file_lock structure according to the contents of the user's flock structure.
5. Terminates returning an error code if the file does not allow the access mode specified by the type of the requested lock.
Invokes the lock method of the file operations, if defined.
a. Invokes posix_locks_conflict( ) for each fl_posix lock in the inode's lock list. The function checks whether the lock conflicts with the requested one. Essentially, there must be no fl_posix write lock for the same region in the inode list, and there may be no fl_posix lock at all for the same region if the process is requesting a write lock. However, locks owned by the same process never conflict; this allows a process to change the characteristics of a lock it already owns.
b. If a conflicting lock is found and fcntl( ) was invoked with the f_setlk or F_SETLK64 flag, returns an error code. Otherwise, the current process should be suspended. In this case, invokes posix_locks_deadlock( ) to check that no deadlock condition is being created among processes waiting for fl_posix locks, and then inserts the current process in the circular list of blocked processes and suspends it.
c. As soon as the inode's lock list includes no conflicting lock, checks all the fl_posix locks of the current process that overlap the file region that the current process wants to lock, and combines and splits adjacent areas as required. For example, if the process requested a write lock for a file region that falls inside a read-locked wider region, the previous read lock is split into two parts covering the nonoverlapping areas, while the central region is protected by the new write lock. In case of overlaps, newer locks always replace older ones.
d. Inserts the new file_lock structure in the global lock list and in the inode list.
8. Returns the value 0 (success).
Was this article helpful?