The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/8255

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Calling `Reset()` from `Create()` was causing a deadlock causing `lxc stop -f` requests to hang if initiated while an `lxc stop` was in progress.
From 678cfbde4804df2c7d50f73b66c03b0be4491fb6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 15 Dec 2020 16:22:56 +0000
Subject: [PATCH 1/3] lxd/instance/operationlock: Fixes deadlock caused by call
 to Reset in Create

Both try to aquire lock and so can deadlock each other.

By pushing to the reset channel directly from Create we avoid the deadlock.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/operationlock/operationlock.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/instance/operationlock/operationlock.go 
b/lxd/instance/operationlock/operationlock.go
index faab4c5982..a3bbd74e04 100644
--- a/lxd/instance/operationlock/operationlock.go
+++ b/lxd/instance/operationlock/operationlock.go
@@ -37,7 +37,8 @@ func Create(instanceID int, action string, reusable bool, 
reuse bool) (*Instance
        op := instanceOperations[instanceID]
        if op != nil {
                if op.reusable && reuse {
-                       op.Reset()
+                       // Reset operation timeout without releasing lock or 
deadlocking using Reset() function.
+                       op.chanReset <- true
                        return op, nil
                }
 

From b8d7f56488219f0dfd3cb0e6076ea9b3b506863e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 15 Dec 2020 16:23:45 +0000
Subject: [PATCH 2/3] lxd/instance/operationlock: Store operation in
 instanceOperations before calling go routine

As the go routine can call functions on the operation (such as op.Done) which 
rely on the instanceOperations map being populated it seems appropriate to 
ensure it has been populated with the new operation before starting the go 
routine.

Even though the only current use of the operation inside the go routine is 
after 30s.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/operationlock/operationlock.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/instance/operationlock/operationlock.go 
b/lxd/instance/operationlock/operationlock.go
index a3bbd74e04..b7e6504961 100644
--- a/lxd/instance/operationlock/operationlock.go
+++ b/lxd/instance/operationlock/operationlock.go
@@ -52,6 +52,8 @@ func Create(instanceID int, action string, reusable bool, 
reuse bool) (*Instance
        op.chanDone = make(chan error, 0)
        op.chanReset = make(chan bool, 0)
 
+       instanceOperations[instanceID] = op
+
        go func(op *InstanceOperation) {
                for {
                        select {
@@ -64,8 +66,6 @@ func Create(instanceID int, action string, reusable bool, 
reuse bool) (*Instance
                }
        }(op)
 
-       instanceOperations[instanceID] = op
-
        return op, nil
 }
 

From 18ace0e0607b0b0f15bf9dc28c6355f57bb66fde Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 15 Dec 2020 16:25:13 +0000
Subject: [PATCH 3/3] lxd/instance/operationlock: Exit go routine started in
 Create when the operation is done

Otherwise I have observed that go routines can hang around for up to 30s after 
operation is completed.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/operationlock/operationlock.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lxd/instance/operationlock/operationlock.go 
b/lxd/instance/operationlock/operationlock.go
index b7e6504961..49dab48b1a 100644
--- a/lxd/instance/operationlock/operationlock.go
+++ b/lxd/instance/operationlock/operationlock.go
@@ -57,6 +57,8 @@ func Create(instanceID int, action string, reusable bool, 
reuse bool) (*Instance
        go func(op *InstanceOperation) {
                for {
                        select {
+                       case <-op.chanDone:
+                               return
                        case <-op.chanReset:
                                continue
                        case <-time.After(time.Second * 30):
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to