| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2016, 2017, 2018, 2019 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-05-17 05:31:28 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 	pathutil "path" | 
					
						
							| 
									
										
										
										
											2018-01-17 23:24:46 +08:00
										 |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	"github.com/minio/dsync/v2" | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	"github.com/minio/lsync" | 
					
						
							| 
									
										
										
										
											2019-05-30 07:35:12 +08:00
										 |  |  | 	"github.com/minio/minio-go/v6/pkg/set" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	xnet "github.com/minio/minio/pkg/net" | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | // Global name space lock.
 | 
					
						
							| 
									
										
										
										
											2016-12-11 08:15:12 +08:00
										 |  |  | var globalNSMutex *nsLockMap | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 02:25:10 +08:00
										 |  |  | // Global lock server one per server.
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | var globalLockServer *lockRESTServer | 
					
						
							| 
									
										
										
										
											2018-01-23 02:25:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Instance of dsync for distributed clients.
 | 
					
						
							|  |  |  | var globalDsync *dsync.Dsync | 
					
						
							| 
									
										
										
										
											2017-06-06 03:25:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | // RWLocker - locker interface to introduce GetRLock, RUnlock.
 | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | type RWLocker interface { | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	GetLock(timeout *dynamicTimeout) (timedOutErr error) | 
					
						
							|  |  |  | 	Unlock() | 
					
						
							|  |  |  | 	GetRLock(timeout *dynamicTimeout) (timedOutErr error) | 
					
						
							|  |  |  | 	RUnlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | // Initialize distributed locking only in case of distributed setup.
 | 
					
						
							| 
									
										
										
										
											2017-06-06 03:25:04 +08:00
										 |  |  | // Returns lock clients and the node index for the current server.
 | 
					
						
							|  |  |  | func newDsyncNodes(endpoints EndpointList) (clnts []dsync.NetLocker, myNode int) { | 
					
						
							|  |  |  | 	myNode = -1 | 
					
						
							| 
									
										
										
										
											2018-01-23 02:25:10 +08:00
										 |  |  | 	seenHosts := set.NewStringSet() | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		if seenHosts.Contains(endpoint.Host) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		seenHosts.Add(endpoint.Host) | 
					
						
							| 
									
										
										
										
											2017-06-06 03:25:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		var locker dsync.NetLocker | 
					
						
							|  |  |  | 		if endpoint.IsLocal { | 
					
						
							|  |  |  | 			myNode = len(clnts) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 			receiver := &lockRESTServer{ | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 				ll: &localLocker{ | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 					serverAddr:      endpoint.Host, | 
					
						
							|  |  |  | 					serviceEndpoint: lockServicePath, | 
					
						
							|  |  |  | 					lockMap:         make(map[string][]lockRequesterInfo), | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			globalLockServer = receiver | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 			locker = receiver.ll | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			host, err := xnet.ParseHost(endpoint.Host) | 
					
						
							| 
									
										
										
										
											2018-09-22 07:04:11 +08:00
										 |  |  | 			logger.FatalIf(err, "Unable to parse Lock RPC Host") | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 			locker = newlockRESTClient(host) | 
					
						
							| 
									
										
										
										
											2017-06-06 03:25:04 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		clnts = append(clnts, locker) | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-06 03:25:04 +08:00
										 |  |  | 	return clnts, myNode | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | // newNSLock - return a new name space lock map.
 | 
					
						
							|  |  |  | func newNSLock(isDistXL bool) *nsLockMap { | 
					
						
							|  |  |  | 	nsMutex := nsLockMap{ | 
					
						
							| 
									
										
										
										
											2016-12-11 08:15:12 +08:00
										 |  |  | 		isDistXL: isDistXL, | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | 	if isDistXL { | 
					
						
							|  |  |  | 		return &nsMutex | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	nsMutex.lockMap = make(map[nsParam]*nsLock) | 
					
						
							| 
									
										
										
										
											2018-01-13 12:34:52 +08:00
										 |  |  | 	return &nsMutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // initNSLock - initialize name space lock map.
 | 
					
						
							|  |  |  | func initNSLock(isDistXL bool) { | 
					
						
							|  |  |  | 	globalNSMutex = newNSLock(isDistXL) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | // nsParam - carries name space resource.
 | 
					
						
							|  |  |  | type nsParam struct { | 
					
						
							|  |  |  | 	volume string | 
					
						
							|  |  |  | 	path   string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // nsLock - provides primitives for locking critical namespace regions.
 | 
					
						
							|  |  |  | type nsLock struct { | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | 	*lsync.LRWMutex | 
					
						
							| 
									
										
										
										
											2016-09-16 15:30:55 +08:00
										 |  |  | 	ref uint | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // nsLockMap - namespace lock map, provides primitives to Lock,
 | 
					
						
							|  |  |  | // Unlock, RLock and RUnlock.
 | 
					
						
							|  |  |  | type nsLockMap struct { | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	// Indicates if namespace is part of a distributed setup.
 | 
					
						
							| 
									
										
										
										
											2016-12-11 08:15:12 +08:00
										 |  |  | 	isDistXL     bool | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | 	lockMap      map[nsParam]*nsLock | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 	lockMapMutex sync.RWMutex | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | // Lock the namespace resource.
 | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | func (n *nsLockMap) lock(ctx context.Context, volume, path string, lockSource, opsID string, readLock bool, timeout time.Duration) (locked bool) { | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | 	var nsLk *nsLock | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 	n.lockMapMutex.Lock() | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 	param := nsParam{volume, path} | 
					
						
							|  |  |  | 	nsLk, found := n.lockMap[param] | 
					
						
							|  |  |  | 	if !found { | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 		n.lockMap[param] = &nsLock{ | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 			LRWMutex: lsync.NewLRWMutex(ctx), | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | 			ref:      1, | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 		nsLk = n.lockMap[param] | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// Update ref count here to avoid multiple races.
 | 
					
						
							|  |  |  | 		nsLk.ref++ | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-11 04:08:11 +08:00
										 |  |  | 	n.lockMapMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	// Locking here will block (until timeout).
 | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 	if readLock { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 		locked = nsLk.GetRLock(opsID, lockSource, timeout) | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 		locked = nsLk.GetLock(opsID, lockSource, timeout) | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !locked { // We failed to get the lock
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 		// Decrement ref count since we failed to get the lock
 | 
					
						
							|  |  |  | 		n.lockMapMutex.Lock() | 
					
						
							|  |  |  | 		nsLk.ref-- | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 		if nsLk.ref == 0 { | 
					
						
							|  |  |  | 			// Remove from the map if there are no more references.
 | 
					
						
							|  |  |  | 			delete(n.lockMap, param) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 		n.lockMapMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	return | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | // Unlock the namespace resource.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | func (n *nsLockMap) unlock(volume, path, opsID string, readLock bool) { | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 	param := nsParam{volume, path} | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 	n.lockMapMutex.RLock() | 
					
						
							|  |  |  | 	nsLk, found := n.lockMap[param] | 
					
						
							|  |  |  | 	n.lockMapMutex.RUnlock() | 
					
						
							|  |  |  | 	if !found { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if readLock { | 
					
						
							|  |  |  | 		nsLk.RUnlock() | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		nsLk.Unlock() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	n.lockMapMutex.Lock() | 
					
						
							|  |  |  | 	if nsLk.ref == 0 { | 
					
						
							|  |  |  | 		logger.LogIf(context.Background(), errors.New("Namespace reference count cannot be 0")) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		nsLk.ref-- | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 		if nsLk.ref == 0 { | 
					
						
							|  |  |  | 			// Remove from the map if there are no more references.
 | 
					
						
							|  |  |  | 			delete(n.lockMap, param) | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-06 11:25:25 +08:00
										 |  |  | 	n.lockMapMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | // Lock - locks the given resource for writes, using a previously
 | 
					
						
							|  |  |  | // allocated name space lock or initializing a new one.
 | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | func (n *nsLockMap) Lock(volume, path, opsID string, timeout time.Duration) (locked bool) { | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	readLock := false // This is a write lock.
 | 
					
						
							| 
									
										
										
										
											2016-09-28 05:35:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-24 07:36:00 +08:00
										 |  |  | 	lockSource := getSource() // Useful for debugging
 | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	return n.lock(context.Background(), volume, path, lockSource, opsID, readLock, timeout) | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | // Unlock - unlocks any previously acquired write locks.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | func (n *nsLockMap) Unlock(volume, path, opsID string) { | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 	readLock := false | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	n.unlock(volume, path, opsID, readLock) | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | // RLock - locks any previously acquired read locks.
 | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | func (n *nsLockMap) RLock(volume, path, opsID string, timeout time.Duration) (locked bool) { | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 	readLock := true | 
					
						
							| 
									
										
										
										
											2016-09-28 05:35:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-24 07:36:00 +08:00
										 |  |  | 	lockSource := getSource() // Useful for debugging
 | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	return n.lock(context.Background(), volume, path, lockSource, opsID, readLock, timeout) | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RUnlock - unlocks any previously acquired read locks.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | func (n *nsLockMap) RUnlock(volume, path, opsID string) { | 
					
						
							| 
									
										
										
										
											2016-04-30 02:39:20 +08:00
										 |  |  | 	readLock := true | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	n.unlock(volume, path, opsID, readLock) | 
					
						
							| 
									
										
										
										
											2016-04-29 16:29:09 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-10-20 00:27:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ForceUnlock - forcefully unlock a lock based on name.
 | 
					
						
							|  |  |  | func (n *nsLockMap) ForceUnlock(volume, path string) { | 
					
						
							|  |  |  | 	n.lockMapMutex.Lock() | 
					
						
							|  |  |  | 	defer n.lockMapMutex.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-21 04:15:28 +08:00
										 |  |  | 	// Clarification on operation:
 | 
					
						
							| 
									
										
										
										
											2016-12-11 08:15:12 +08:00
										 |  |  | 	// - In case of FS or XL we call ForceUnlock on the local globalNSMutex
 | 
					
						
							| 
									
										
										
										
											2016-10-21 04:15:28 +08:00
										 |  |  | 	//   (since there is only a single server) which will cause the 'stuck'
 | 
					
						
							|  |  |  | 	//   mutex to be removed from the map. Existing operations for this
 | 
					
						
							|  |  |  | 	//   will continue to be blocked (and timeout). New operations on this
 | 
					
						
							|  |  |  | 	//   resource will use a new mutex and proceed normally.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// - In case of Distributed setup (using dsync), there is no need to call
 | 
					
						
							|  |  |  | 	//   ForceUnlock on the server where the lock was acquired and is presumably
 | 
					
						
							|  |  |  | 	//   'stuck'. Instead dsync.ForceUnlock() will release the underlying locks
 | 
					
						
							|  |  |  | 	//   that participated in granting the lock. Any pending dsync locks that
 | 
					
						
							|  |  |  | 	//   are blocking can now proceed as normal and any new locks will also
 | 
					
						
							|  |  |  | 	//   participate normally.
 | 
					
						
							| 
									
										
										
										
											2016-12-11 08:15:12 +08:00
										 |  |  | 	if n.isDistXL { // For distributed mode, broadcast ForceUnlock message.
 | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 		dsync.NewDRWMutex(context.Background(), pathJoin(volume, path), globalDsync).ForceUnlock() | 
					
						
							| 
									
										
										
										
											2016-10-20 00:27:36 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 	// Remove lock from the map.
 | 
					
						
							|  |  |  | 	delete(n.lockMap, nsParam{volume, path}) | 
					
						
							| 
									
										
										
										
											2016-10-20 00:27:36 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | // dsync's distributed lock instance.
 | 
					
						
							|  |  |  | type distLockInstance struct { | 
					
						
							|  |  |  | 	rwMutex             *dsync.DRWMutex | 
					
						
							|  |  |  | 	volume, path, opsID string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Lock - block until write lock is taken or timeout has occurred.
 | 
					
						
							|  |  |  | func (di *distLockInstance) GetLock(timeout *dynamicTimeout) (timedOutErr error) { | 
					
						
							|  |  |  | 	lockSource := getSource() | 
					
						
							|  |  |  | 	start := UTCNow() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !di.rwMutex.GetLock(di.opsID, lockSource, timeout.Timeout()) { | 
					
						
							|  |  |  | 		timeout.LogFailure() | 
					
						
							|  |  |  | 		return OperationTimedOut{Path: di.path} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	timeout.LogSuccess(UTCNow().Sub(start)) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Unlock - block until write lock is released.
 | 
					
						
							|  |  |  | func (di *distLockInstance) Unlock() { | 
					
						
							|  |  |  | 	di.rwMutex.Unlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RLock - block until read lock is taken or timeout has occurred.
 | 
					
						
							|  |  |  | func (di *distLockInstance) GetRLock(timeout *dynamicTimeout) (timedOutErr error) { | 
					
						
							|  |  |  | 	lockSource := getSource() | 
					
						
							|  |  |  | 	start := UTCNow() | 
					
						
							|  |  |  | 	if !di.rwMutex.GetRLock(di.opsID, lockSource, timeout.Timeout()) { | 
					
						
							|  |  |  | 		timeout.LogFailure() | 
					
						
							|  |  |  | 		return OperationTimedOut{Path: di.path} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	timeout.LogSuccess(UTCNow().Sub(start)) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RUnlock - block until read lock is released.
 | 
					
						
							|  |  |  | func (di *distLockInstance) RUnlock() { | 
					
						
							|  |  |  | 	di.rwMutex.RUnlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // localLockInstance - frontend/top-level interface for namespace locks.
 | 
					
						
							|  |  |  | type localLockInstance struct { | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	ctx                 context.Context | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	ns                  *nsLockMap | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | 	volume, path, opsID string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewNSLock - returns a lock instance for a given volume and
 | 
					
						
							|  |  |  | // path. The returned lockInstance object encapsulates the nsLockMap,
 | 
					
						
							|  |  |  | // volume, path and operation ID.
 | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | func (n *nsLockMap) NewNSLock(ctx context.Context, volume, path string) RWLocker { | 
					
						
							| 
									
										
										
										
											2018-08-03 01:39:42 +08:00
										 |  |  | 	opsID := mustGetUUID() | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | 	if n.isDistXL { | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 		return &distLockInstance{dsync.NewDRWMutex(ctx, pathJoin(volume, path), globalDsync), volume, path, opsID} | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	return &localLockInstance{ctx, n, volume, path, opsID} | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | // Lock - block until write lock is taken or timeout has occurred.
 | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | func (li *localLockInstance) GetLock(timeout *dynamicTimeout) (timedOutErr error) { | 
					
						
							| 
									
										
										
										
											2017-03-24 07:36:00 +08:00
										 |  |  | 	lockSource := getSource() | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	start := UTCNow() | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | 	readLock := false | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	if !li.ns.lock(li.ctx, li.volume, li.path, lockSource, li.opsID, readLock, timeout.Timeout()) { | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 		timeout.LogFailure() | 
					
						
							|  |  |  | 		return OperationTimedOut{Path: li.path} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	timeout.LogSuccess(UTCNow().Sub(start)) | 
					
						
							|  |  |  | 	return | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Unlock - block until write lock is released.
 | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | func (li *localLockInstance) Unlock() { | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | 	readLock := false | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	li.ns.unlock(li.volume, li.path, li.opsID, readLock) | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | // RLock - block until read lock is taken or timeout has occurred.
 | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | func (li *localLockInstance) GetRLock(timeout *dynamicTimeout) (timedOutErr error) { | 
					
						
							| 
									
										
										
										
											2017-03-24 07:36:00 +08:00
										 |  |  | 	lockSource := getSource() | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 	start := UTCNow() | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | 	readLock := true | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	if !li.ns.lock(li.ctx, li.volume, li.path, lockSource, li.opsID, readLock, timeout.Timeout()) { | 
					
						
							| 
									
										
										
										
											2017-09-01 02:29:22 +08:00
										 |  |  | 		timeout.LogFailure() | 
					
						
							|  |  |  | 		return OperationTimedOut{Path: li.path} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	timeout.LogSuccess(UTCNow().Sub(start)) | 
					
						
							|  |  |  | 	return | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RUnlock - block until read lock is released.
 | 
					
						
							| 
									
										
										
										
											2019-04-03 03:27:20 +08:00
										 |  |  | func (li *localLockInstance) RUnlock() { | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | 	readLock := true | 
					
						
							| 
									
										
										
										
											2017-01-17 09:05:00 +08:00
										 |  |  | 	li.ns.unlock(li.volume, li.path, li.opsID, readLock) | 
					
						
							| 
									
										
										
										
											2016-11-10 02:58:41 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-17 23:24:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func getSource() string { | 
					
						
							|  |  |  | 	var funcName string | 
					
						
							|  |  |  | 	pc, filename, lineNum, ok := runtime.Caller(2) | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		filename = pathutil.Base(filename) | 
					
						
							|  |  |  | 		funcName = strings.TrimPrefix(runtime.FuncForPC(pc).Name(), | 
					
						
							|  |  |  | 			"github.com/minio/minio/cmd.") | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		filename = "<unknown>" | 
					
						
							|  |  |  | 		lineNum = 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fmt.Sprintf("[%s:%d:%s()]", filename, lineNum, funcName) | 
					
						
							|  |  |  | } |