Browse code

Refcount graphdriver plugins properly

Adds 2 new methods to v2 plugin `Acquire` and `Release` which allow
refcounting directly at the plugin level instead of just the store.
Since a graphdriver is initialized exactly once, and is really managed
by a separate object, it didn't really seem right to call
`getter.Get()` to refcount graphdriver plugins.
On shutdown it was particularly weird where we'd either need to keep a
driver reference in daemon, or keep a reference to the pluggin getter in
the layer store, and even then still store extra details on if the
graphdriver is a plugin or not.

Instead the plugin proxy itself will handle calling the neccessary
refcounting methods directly on the plugin object.

Also adds a new interface in `plugingetter` to account for these new
functions which are not going to be implemented by v1 plugins.

Changes terms `plugingetter.CREATE` and `plugingetter.REMOVE` to
`ACQUIRE` and `RELEASE` respectively, which seems to be better
adjectives for what we're doing.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2016/12/01 06:02:45
Showing 5 changed files
... ...
@@ -22,10 +22,10 @@ func lookupPlugin(name, home string, opts []string, pg plugingetter.PluginGetter
22 22
 	if err != nil {
23 23
 		return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
24 24
 	}
25
-	return newPluginDriver(name, home, opts, pl.Client())
25
+	return newPluginDriver(name, home, opts, pl)
26 26
 }
27 27
 
28
-func newPluginDriver(name, home string, opts []string, c pluginClient) (Driver, error) {
29
-	proxy := &graphDriverProxy{name, c}
28
+func newPluginDriver(name, home string, opts []string, pl plugingetter.CompatPlugin) (Driver, error) {
29
+	proxy := &graphDriverProxy{name, pl.Client(), pl}
30 30
 	return proxy, proxy.Init(filepath.Join(home, name), opts)
31 31
 }
... ...
@@ -6,11 +6,13 @@ import (
6 6
 	"io"
7 7
 
8 8
 	"github.com/docker/docker/pkg/archive"
9
+	"github.com/docker/docker/pkg/plugingetter"
9 10
 )
10 11
 
11 12
 type graphDriverProxy struct {
12 13
 	name   string
13 14
 	client pluginClient
15
+	p      plugingetter.CompatPlugin
14 16
 }
15 17
 
16 18
 type graphDriverRequest struct {
... ...
@@ -35,6 +37,12 @@ type graphDriverInitRequest struct {
35 35
 }
36 36
 
37 37
 func (d *graphDriverProxy) Init(home string, opts []string) error {
38
+	if !d.p.IsV1() {
39
+		if cp, ok := d.p.(plugingetter.CountedPlugin); ok {
40
+			// always acquire here, it will be cleaned up on daemon shutdown
41
+			cp.Acquire()
42
+		}
43
+	}
38 44
 	args := &graphDriverInitRequest{
39 45
 		Home: home,
40 46
 		Opts: opts,
... ...
@@ -167,6 +175,13 @@ func (d *graphDriverProxy) GetMetadata(id string) (map[string]string, error) {
167 167
 }
168 168
 
169 169
 func (d *graphDriverProxy) Cleanup() error {
170
+	if !d.p.IsV1() {
171
+		if cp, ok := d.p.(plugingetter.CountedPlugin); ok {
172
+			// always release
173
+			defer cp.Release()
174
+		}
175
+	}
176
+
170 177
 	args := &graphDriverRequest{}
171 178
 	var ret graphDriverResponse
172 179
 	if err := d.client.Call("GraphDriver.Cleanup", args, &ret); err != nil {
... ...
@@ -5,10 +5,10 @@ import "github.com/docker/docker/pkg/plugins"
5 5
 const (
6 6
 	// LOOKUP doesn't update RefCount
7 7
 	LOOKUP = 0
8
-	// CREATE increments RefCount
9
-	CREATE = 1
10
-	// REMOVE decrements RefCount
11
-	REMOVE = -1
8
+	// ACQUIRE increments RefCount
9
+	ACQUIRE = 1
10
+	// RELEASE decrements RefCount
11
+	RELEASE = -1
12 12
 )
13 13
 
14 14
 // CompatPlugin is a abstraction to handle both v2(new) and v1(legacy) plugins.
... ...
@@ -19,6 +19,13 @@ type CompatPlugin interface {
19 19
 	IsV1() bool
20 20
 }
21 21
 
22
+// CountedPlugin is a plugin which is reference counted.
23
+type CountedPlugin interface {
24
+	Acquire()
25
+	Release()
26
+	CompatPlugin
27
+}
28
+
22 29
 // PluginGetter is the interface implemented by Store
23 30
 type PluginGetter interface {
24 31
 	Get(name, capability string, mode int) (CompatPlugin, error)
... ...
@@ -11,6 +11,7 @@ import (
11 11
 
12 12
 	"github.com/docker/docker/api/types"
13 13
 	"github.com/docker/docker/oci"
14
+	"github.com/docker/docker/pkg/plugingetter"
14 15
 	"github.com/docker/docker/pkg/plugins"
15 16
 	"github.com/docker/docker/pkg/system"
16 17
 	specs "github.com/opencontainers/runtime-spec/specs-go"
... ...
@@ -294,6 +295,19 @@ func (p *Plugin) AddRefCount(count int) {
294 294
 	p.refCount += count
295 295
 }
296 296
 
297
+// Acquire increments the plugin's reference count
298
+// This should be followed up by `Release()` when the plugin is no longer in use.
299
+func (p *Plugin) Acquire() {
300
+	p.AddRefCount(plugingetter.ACQUIRE)
301
+}
302
+
303
+// Release decrements the plugin's reference count
304
+// This should only be called when the plugin is no longer in use, e.g. with
305
+// via `Acquire()` or getter.Get("name", "type", plugingetter.ACQUIRE)
306
+func (p *Plugin) Release() {
307
+	p.AddRefCount(plugingetter.RELEASE)
308
+}
309
+
297 310
 // InitSpec creates an OCI spec from the plugin's config.
298 311
 func (p *Plugin) InitSpec(s specs.Spec) (*specs.Spec, error) {
299 312
 	s.Root = specs.Root{
... ...
@@ -153,7 +153,7 @@ func CreateDriver(name string) (volume.Driver, error) {
153 153
 	if name == "" {
154 154
 		name = volume.DefaultDriverName
155 155
 	}
156
-	return lookup(name, getter.CREATE)
156
+	return lookup(name, getter.ACQUIRE)
157 157
 }
158 158
 
159 159
 // RemoveDriver returns a volume driver by its name and decrements RefCount..
... ...
@@ -162,7 +162,7 @@ func RemoveDriver(name string) (volume.Driver, error) {
162 162
 	if name == "" {
163 163
 		name = volume.DefaultDriverName
164 164
 	}
165
-	return lookup(name, getter.REMOVE)
165
+	return lookup(name, getter.RELEASE)
166 166
 }
167 167
 
168 168
 // GetDriverList returns list of volume drivers registered.