[PATCHv2,4/5] locks: Add leases c/r for kernels v4.0 and older

Submitted by Pavel Begunkov on Aug. 13, 2017, 11:49 p.m.

Details

Message ID 20170813234941.26329-4-asml.silence@gmail.com
State New
Series "Series without cover letter"
Headers show

Commit Message

Pavel Begunkov Aug. 13, 2017, 11:49 p.m.
Information about locks in /proc/<pid>/fdinfo is presented only since
kernel v4.1. This patch adds logic to *note_file_lock* to match leases
and OFDs.

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
 criu/file-lock.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 83 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/file-lock.c b/criu/file-lock.c
index 5b0580782..b7462dc77 100644
--- a/criu/file-lock.c
+++ b/criu/file-lock.c
@@ -266,6 +266,81 @@  static int lock_ofd_check_fd(int lfd, struct file_lock *fl)
 	return 1;
 }
 
+static int lease_check_fd(int fd, int file_flags, struct file_lock *fl)
+{
+	int file_lease_type, err;
+	int lease_type = fl->fl_ltype & (~LEASE_BREAKING);
+
+	if ((file_flags & O_ACCMODE) != O_RDONLY) {
+		/*
+		 * Write OFD conflicts with any lease not associated
+		 * with it, therefore there is can't be other lease
+		 * or OFD for this file.
+		 */
+		return 1;
+	}
+
+	file_lease_type = fcntl(fd, F_GETLEASE);
+	if (file_lease_type < 0) {
+		pr_err("Can't get lease type\n");
+		return -1;
+	}
+
+	/*
+	 * Only read OFDs can be present for the file. If
+	 * read and write OFDs with at least one lease had
+	 * presented, it would have conflicted.
+	 */
+	if (fl->fl_ltype & LEASE_BREAKING) {
+		/*
+		 * Only read leases are possible for read OFDs
+		 * and they all should be in breaking state,
+		 * because the current one is.
+		 */
+		int compatible_type = file_lease_type;
+
+		if (compatible_type != F_UNLCK) {
+			pr_err("Lease doesn't conflicts but breaks\n");
+			return -1;
+		}
+
+		/*
+		 * Due to activated breaking sequence we can't
+		 * get actual lease type with F_GETLEASE.
+		 * The err == 0 after lease upgrade means, that
+		 * there is already read lease. Otherwise it
+		 * will fail because of breaking sequence.
+		 */
+		err = fcntl(fd, F_SETLEASE, F_RDLCK);
+		if (err < 0) {
+			if (errno != EAGAIN) {
+				pr_perror("Can't set lease (fd %i)", fd);
+				return -1;
+			}
+			return 0;
+		} else {
+			return 1;
+		}
+	} else {
+		/*
+		 * The file can have only non-breaking read
+		 * leases, because otherwise the current one
+		 * also would have broke.
+		 */
+		if (lease_type != F_RDLCK) {
+			pr_err("Incorrect lease type\n");
+			return -1;
+		}
+
+		if (file_lease_type == F_UNLCK)
+			return 0;
+		if (file_lease_type == F_RDLCK)
+			return 1;
+		pr_err("Invalid file lease type\n");
+		return -1;
+	}
+}
+
 int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
 {
 	struct file_lock *fl;
@@ -295,8 +370,14 @@  int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
 			if (fl->fl_owner != pid->real)
 				continue;
 		} else if (fl->fl_kind == FL_LEASE) {
-			pr_err("Leases are not supported for kernel <= v4.0");
-			return -1;
+			if (fl->owners_fd >= 0)
+				continue;
+
+			ret = lease_check_fd(lfd, p->flags, fl);
+			if (ret < 0)
+				return ret;
+			if (ret == 0)
+				continue;
 		} else /* fl->fl_kind == FL_FLOCK || fl->fl_kind == FL_OFD */ {
 			int ret;
 

Comments

Pavel Emelyanov Aug. 15, 2017, 2:51 p.m.
On 08/14/2017 02:49 AM, Pavel Begunkov wrote:
> Information about locks in /proc/<pid>/fdinfo is presented only since
> kernel v4.1. This patch adds logic to *note_file_lock* to match leases
> and OFDs.

OFDs? Can you shed more light on this, why have OFDs popped up?

> Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
> ---
>  criu/file-lock.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 83 insertions(+), 2 deletions(-)
> 
> diff --git a/criu/file-lock.c b/criu/file-lock.c
> index 5b0580782..b7462dc77 100644
> --- a/criu/file-lock.c
> +++ b/criu/file-lock.c
> @@ -266,6 +266,81 @@ static int lock_ofd_check_fd(int lfd, struct file_lock *fl)
>  	return 1;
>  }
>  
> +static int lease_check_fd(int fd, int file_flags, struct file_lock *fl)
> +{
> +	int file_lease_type, err;
> +	int lease_type = fl->fl_ltype & (~LEASE_BREAKING);
> +
> +	if ((file_flags & O_ACCMODE) != O_RDONLY) {
> +		/*
> +		 * Write OFD conflicts with any lease not associated
> +		 * with it, therefore there is can't be other lease
> +		 * or OFD for this file.
> +		 */
> +		return 1;
> +	}
> +
> +	file_lease_type = fcntl(fd, F_GETLEASE);
> +	if (file_lease_type < 0) {
> +		pr_err("Can't get lease type\n");
> +		return -1;
> +	}
> +
> +	/*
> +	 * Only read OFDs can be present for the file. If
> +	 * read and write OFDs with at least one lease had
> +	 * presented, it would have conflicted.
> +	 */
> +	if (fl->fl_ltype & LEASE_BREAKING) {
> +		/*
> +		 * Only read leases are possible for read OFDs
> +		 * and they all should be in breaking state,
> +		 * because the current one is.
> +		 */
> +		int compatible_type = file_lease_type;
> +
> +		if (compatible_type != F_UNLCK) {
> +			pr_err("Lease doesn't conflicts but breaks\n");
> +			return -1;
> +		}
> +
> +		/*
> +		 * Due to activated breaking sequence we can't
> +		 * get actual lease type with F_GETLEASE.
> +		 * The err == 0 after lease upgrade means, that
> +		 * there is already read lease. Otherwise it
> +		 * will fail because of breaking sequence.
> +		 */
> +		err = fcntl(fd, F_SETLEASE, F_RDLCK);
> +		if (err < 0) {
> +			if (errno != EAGAIN) {
> +				pr_perror("Can't set lease (fd %i)", fd);
> +				return -1;
> +			}
> +			return 0;
> +		} else {
> +			return 1;
> +		}
> +	} else {
> +		/*
> +		 * The file can have only non-breaking read
> +		 * leases, because otherwise the current one
> +		 * also would have broke.
> +		 */
> +		if (lease_type != F_RDLCK) {
> +			pr_err("Incorrect lease type\n");
> +			return -1;
> +		}
> +
> +		if (file_lease_type == F_UNLCK)
> +			return 0;
> +		if (file_lease_type == F_RDLCK)
> +			return 1;
> +		pr_err("Invalid file lease type\n");
> +		return -1;
> +	}
> +}
> +
>  int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
>  {
>  	struct file_lock *fl;
> @@ -295,8 +370,14 @@ int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
>  			if (fl->fl_owner != pid->real)
>  				continue;
>  		} else if (fl->fl_kind == FL_LEASE) {
> -			pr_err("Leases are not supported for kernel <= v4.0");
> -			return -1;
> +			if (fl->owners_fd >= 0)
> +				continue;
> +
> +			ret = lease_check_fd(lfd, p->flags, fl);
> +			if (ret < 0)
> +				return ret;
> +			if (ret == 0)
> +				continue;
>  		} else /* fl->fl_kind == FL_FLOCK || fl->fl_kind == FL_OFD */ {
>  			int ret;
>  
>
Pavel Begunkov Aug. 16, 2017, 12:22 p.m.
On 15/08/17 17:51, Pavel Emelyanov wrote:
> On 08/14/2017 02:49 AM, Pavel Begunkov wrote:
>> Information about locks in /proc/<pid>/fdinfo is presented only since
>> kernel v4.1. This patch adds logic to *note_file_lock* to match leases
>> and OFDs.
> 
> OFDs? Can you shed more light on this, why have OFDs popped up?
It is what the note_file_locks do.

*note_file_lock* is used only if there is no info about locks in
proc/<pid>/fdinfo and we are forced to use /proc/locks. The last one
tells physical file, but not the OFD with which the lease is associated.
There are can be several OFDs with different locks for the same physical
file. So, *note_file_lock* is restoring this association by setting
owner_fd field for each lock. Yeah, it can be several fd's for the OFD,
but we actually don't care which one will be chosen as lock owner while
it belongs to a right process.

In result, we are associating locks with OFDs by setting proper fd.

> 
>> Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
>> ---
>>  criu/file-lock.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
>>  1 file changed, 83 insertions(+), 2 deletions(-)
>>
>> diff --git a/criu/file-lock.c b/criu/file-lock.c
>> index 5b0580782..b7462dc77 100644
>> --- a/criu/file-lock.c
>> +++ b/criu/file-lock.c
>> @@ -266,6 +266,81 @@ static int lock_ofd_check_fd(int lfd, struct file_lock *fl)
>>  	return 1;
>>  }
>>  
>> +static int lease_check_fd(int fd, int file_flags, struct file_lock *fl)
>> +{
>> +	int file_lease_type, err;
>> +	int lease_type = fl->fl_ltype & (~LEASE_BREAKING);
>> +
>> +	if ((file_flags & O_ACCMODE) != O_RDONLY) {
>> +		/*
>> +		 * Write OFD conflicts with any lease not associated
>> +		 * with it, therefore there is can't be other lease
>> +		 * or OFD for this file.
>> +		 */
>> +		return 1;
>> +	}
>> +
>> +	file_lease_type = fcntl(fd, F_GETLEASE);
>> +	if (file_lease_type < 0) {
>> +		pr_err("Can't get lease type\n");
>> +		return -1;
>> +	}
>> +
>> +	/*
>> +	 * Only read OFDs can be present for the file. If
>> +	 * read and write OFDs with at least one lease had
>> +	 * presented, it would have conflicted.
>> +	 */
>> +	if (fl->fl_ltype & LEASE_BREAKING) {
>> +		/*
>> +		 * Only read leases are possible for read OFDs
>> +		 * and they all should be in breaking state,
>> +		 * because the current one is.
>> +		 */
>> +		int compatible_type = file_lease_type;
>> +
>> +		if (compatible_type != F_UNLCK) {
>> +			pr_err("Lease doesn't conflicts but breaks\n");
>> +			return -1;
>> +		}
>> +
>> +		/*
>> +		 * Due to activated breaking sequence we can't
>> +		 * get actual lease type with F_GETLEASE.
>> +		 * The err == 0 after lease upgrade means, that
>> +		 * there is already read lease. Otherwise it
>> +		 * will fail because of breaking sequence.
>> +		 */
>> +		err = fcntl(fd, F_SETLEASE, F_RDLCK);
>> +		if (err < 0) {
>> +			if (errno != EAGAIN) {
>> +				pr_perror("Can't set lease (fd %i)", fd);
>> +				return -1;
>> +			}
>> +			return 0;
>> +		} else {
>> +			return 1;
>> +		}
>> +	} else {
>> +		/*
>> +		 * The file can have only non-breaking read
>> +		 * leases, because otherwise the current one
>> +		 * also would have broke.
>> +		 */
>> +		if (lease_type != F_RDLCK) {
>> +			pr_err("Incorrect lease type\n");
>> +			return -1;
>> +		}
>> +
>> +		if (file_lease_type == F_UNLCK)
>> +			return 0;
>> +		if (file_lease_type == F_RDLCK)
>> +			return 1;
>> +		pr_err("Invalid file lease type\n");
>> +		return -1;
>> +	}
>> +}
>> +
>>  int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
>>  {
>>  	struct file_lock *fl;
>> @@ -295,8 +370,14 @@ int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
>>  			if (fl->fl_owner != pid->real)
>>  				continue;
>>  		} else if (fl->fl_kind == FL_LEASE) {
>> -			pr_err("Leases are not supported for kernel <= v4.0");
>> -			return -1;
>> +			if (fl->owners_fd >= 0)
>> +				continue;
>> +
>> +			ret = lease_check_fd(lfd, p->flags, fl);
>> +			if (ret < 0)
>> +				return ret;
>> +			if (ret == 0)
>> +				continue;
>>  		} else /* fl->fl_kind == FL_FLOCK || fl->fl_kind == FL_OFD */ {
>>  			int ret;
>>  
>>
>