github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/inode/config.go (about)

     1  package inode
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  	"unsafe"
     8  
     9  	"github.com/swiftstack/cstruct"
    10  	"github.com/swiftstack/sortedmap"
    11  
    12  	"github.com/swiftstack/ProxyFS/blunder"
    13  	"github.com/swiftstack/ProxyFS/conf"
    14  	"github.com/swiftstack/ProxyFS/headhunter"
    15  	"github.com/swiftstack/ProxyFS/logger"
    16  	"github.com/swiftstack/ProxyFS/swiftclient"
    17  	"github.com/swiftstack/ProxyFS/trackedlock"
    18  	"github.com/swiftstack/ProxyFS/transitions"
    19  )
    20  
    21  type readCacheKeyStruct struct {
    22  	volumeName       string
    23  	logSegmentNumber uint64
    24  	cacheLineTag     uint64 // LogSegment offset / readCacheLineSize
    25  }
    26  
    27  type readCacheElementStruct struct {
    28  	readCacheKey readCacheKeyStruct
    29  	next         *readCacheElementStruct // nil if MRU element of volumeGroupStruct.readCache
    30  	prev         *readCacheElementStruct // nil if LRU element of volumeGroupStruct.readCache
    31  	cacheLine    []byte
    32  }
    33  
    34  type volumeGroupStruct struct {
    35  	trackedlock.Mutex
    36  	name                    string
    37  	volumeMap               map[string]*volumeStruct // key == volumeStruct.volumeName
    38  	numServed               uint64
    39  	virtualIPAddr           string
    40  	activePeerPrivateIPAddr string
    41  	readCacheLineSize       uint64
    42  	readCacheWeight         uint64
    43  	readCacheLineCount      uint64
    44  	readCache               map[readCacheKeyStruct]*readCacheElementStruct
    45  	readCacheMRU            *readCacheElementStruct
    46  	readCacheLRU            *readCacheElementStruct
    47  }
    48  
    49  type physicalContainerLayoutStruct struct {
    50  	name                   string
    51  	containerStoragePolicy string   //    [<PhysicalContainerLayout>]ContainerStoragePolicy
    52  	containerNamePrefix    string   //    [<PhysicalContainerLayout>]ContainerNamePrefix
    53  	containerNameSlice     []string //    == slice of current PhysicalContainers for this PhysicalContainerLayout
    54  	//                                       Note: all prefixed by containerNamePrefix
    55  	containersPerPeer           uint64 // [<PhysicalContainerLayout>]ContainersPerPeer
    56  	maxObjectsPerContainer      uint64 // [<PhysicalContainerLayout>]MaxObjectsPerContainer
    57  	containerNameSliceNextIndex uint64 // == next index in nameSlice
    58  	containerNameSliceLoopCount uint64 // == number of times looped through nameSlice
    59  	//                                       Note: if 0 == (containerNameSliceLoopCount mod maxObjectsPerContainer)
    60  	//                                             then we need to re-provision containerNameSlice
    61  }
    62  
    63  type volumeStruct struct {
    64  	trackedlock.Mutex
    65  	volumeGroup                    *volumeGroupStruct
    66  	served                         bool
    67  	fsid                           uint64
    68  	volumeName                     string
    69  	accountName                    string
    70  	maxEntriesPerDirNode           uint64
    71  	maxExtentsPerFileNode          uint64
    72  	defaultPhysicalContainerLayout *physicalContainerLayoutStruct
    73  	maxFlushSize                   uint64
    74  	headhunterVolumeHandle         headhunter.VolumeHandle
    75  	inodeCache                     sortedmap.LLRBTree //          key == InodeNumber; value == *inMemoryInodeStruct
    76  	inodeCacheStopChan             chan struct{}
    77  	inodeCacheWG                   sync.WaitGroup
    78  	inodeCacheLRUHead              *inMemoryInodeStruct
    79  	inodeCacheLRUTail              *inMemoryInodeStruct
    80  	inodeCacheLRUItems             uint64
    81  	inodeCacheLRUMaxBytes          uint64
    82  	inodeCacheLRUTicker            *time.Ticker
    83  	inodeCacheLRUTickerInterval    time.Duration
    84  	snapShotPolicy                 *snapShotPolicyStruct
    85  }
    86  
    87  const (
    88  	defaultNoWriteErrno        = blunder.NoSpaceError
    89  	defaultNoWriteErrnoString  = "ENOSPC"
    90  	defaultReadOnlyErrno       = blunder.ReadOnlyError
    91  	defaultReadOnlyErrnoString = "EROFS"
    92  )
    93  
    94  type globalsStruct struct {
    95  	trackedlock.Mutex
    96  	whoAmI                             string
    97  	myPrivateIPAddr                    string
    98  	dirEntryCache                      sortedmap.BPlusTreeCache
    99  	dirEntryCachePriorCacheHits        uint64
   100  	dirEntryCachePriorCacheMisses      uint64
   101  	fileExtentMapCache                 sortedmap.BPlusTreeCache
   102  	fileExtentMapCachePriorCacheHits   uint64
   103  	fileExtentMapCachePriorCacheMisses uint64
   104  	volumeGroupMap                     map[string]*volumeGroupStruct // key == volumeGroupStruct.name
   105  	volumeMap                          map[string]*volumeStruct      // key == volumeStruct.volumeName
   106  	accountMap                         map[string]*volumeStruct      // key == volumeStruct.accountName
   107  	fileExtentStructSize               uint64                        // pre-calculated size of cstruct-packed fileExtentStruct
   108  	supportedOnDiskInodeVersions       map[Version]struct{}          // key == on disk inode version
   109  	corruptionDetectedTrueBuf          []byte                        // holds serialized CorruptionDetected == true
   110  	corruptionDetectedFalseBuf         []byte                        // holds serialized CorruptionDetected == false
   111  	versionV1Buf                       []byte                        // holds serialized Version            == V1
   112  	inodeRecDefaultPreambleBuf         []byte                        // holds concatenated corruptionDetectedFalseBuf & versionV1Buf
   113  	inodeSize                          uint64                        // size of in-memory inode struct
   114  	openLogSegmentLRUHead              *inFlightLogSegmentStruct
   115  	openLogSegmentLRUTail              *inFlightLogSegmentStruct
   116  	openLogSegmentLRUItems             uint64
   117  	noWriteThresholdErrno              blunder.FsError // either blunder.NotPermError or blunder.ReadOnlyError or blunder.NoSpaceError
   118  	noWriteThresholdErrnoString        string          // either "EPERM" or "EROFS" or "ENOSPC"
   119  	readOnlyThresholdErrno             blunder.FsError // either blunder.NotPermError or blunder.ReadOnlyError or blunder.NoSpaceError
   120  	readOnlyThresholdErrnoString       string          // either "EPERM" or "EROFS" or "ENOSPC"
   121  	rwMode                             RWModeType      // One of RWMode{Normal|NoWrite|ReadOnly}
   122  }
   123  
   124  var globals globalsStruct
   125  
   126  func init() {
   127  	transitions.Register("inode", &globals)
   128  }
   129  
   130  func (dummy *globalsStruct) Up(confMap conf.ConfMap) (err error) {
   131  	var (
   132  		corruptionDetectedFalse     = CorruptionDetected(false)
   133  		corruptionDetectedTrue      = CorruptionDetected(true)
   134  		dirEntryCacheEvictHighLimit uint64
   135  		dirEntryCacheEvictLowLimit  uint64
   136  		fileExtentMapEvictHighLimit uint64
   137  		fileExtentMapEvictLowLimit  uint64
   138  		ok                          bool
   139  		peerName                    string
   140  		peerNames                   []string
   141  		peerPrivateIPAddr           string
   142  		peerPrivateIPAddrMap        map[string]string
   143  		tempInode                   inMemoryInodeStruct
   144  		versionV1                   = Version(V1)
   145  	)
   146  
   147  	peerPrivateIPAddrMap = make(map[string]string)
   148  
   149  	peerNames, err = confMap.FetchOptionValueStringSlice("Cluster", "Peers")
   150  	if nil != err {
   151  		return
   152  	}
   153  
   154  	for _, peerName = range peerNames {
   155  		peerPrivateIPAddr, err = confMap.FetchOptionValueString("Peer:"+peerName, "PrivateIPAddr")
   156  		if nil != err {
   157  			return
   158  		}
   159  
   160  		peerPrivateIPAddrMap[peerName] = peerPrivateIPAddr
   161  	}
   162  
   163  	globals.whoAmI, err = confMap.FetchOptionValueString("Cluster", "WhoAmI")
   164  	if nil != err {
   165  		return
   166  	}
   167  	globals.myPrivateIPAddr, ok = peerPrivateIPAddrMap[globals.whoAmI]
   168  	if !ok {
   169  		err = fmt.Errorf("Cluster.WhoAmI (\"%v\") not in Cluster.Peers list", globals.whoAmI)
   170  		return
   171  	}
   172  
   173  	dirEntryCacheEvictLowLimit, err = confMap.FetchOptionValueUint64("FSGlobals", "DirEntryCacheEvictLowLimit")
   174  	if nil != err {
   175  		return
   176  	}
   177  	dirEntryCacheEvictHighLimit, err = confMap.FetchOptionValueUint64("FSGlobals", "DirEntryCacheEvictHighLimit")
   178  	if nil != err {
   179  		return
   180  	}
   181  
   182  	globals.dirEntryCache = sortedmap.NewBPlusTreeCache(dirEntryCacheEvictLowLimit, dirEntryCacheEvictHighLimit)
   183  
   184  	globals.dirEntryCachePriorCacheHits = 0
   185  	globals.dirEntryCachePriorCacheMisses = 0
   186  
   187  	fileExtentMapEvictLowLimit, err = confMap.FetchOptionValueUint64("FSGlobals", "FileExtentMapEvictLowLimit")
   188  	if nil != err {
   189  		return
   190  	}
   191  	fileExtentMapEvictHighLimit, err = confMap.FetchOptionValueUint64("FSGlobals", "FileExtentMapEvictHighLimit")
   192  	if nil != err {
   193  		return
   194  	}
   195  
   196  	globals.fileExtentMapCache = sortedmap.NewBPlusTreeCache(fileExtentMapEvictLowLimit, fileExtentMapEvictHighLimit)
   197  
   198  	globals.fileExtentMapCachePriorCacheHits = 0
   199  	globals.fileExtentMapCachePriorCacheMisses = 0
   200  
   201  	globals.volumeGroupMap = make(map[string]*volumeGroupStruct)
   202  	globals.volumeMap = make(map[string]*volumeStruct)
   203  	globals.accountMap = make(map[string]*volumeStruct)
   204  
   205  	globals.inodeSize = uint64(unsafe.Sizeof(tempInode))
   206  
   207  	globals.openLogSegmentLRUHead = nil
   208  	globals.openLogSegmentLRUTail = nil
   209  	globals.openLogSegmentLRUItems = 0
   210  
   211  	globals.fileExtentStructSize, _, err = cstruct.Examine(fileExtentStruct{})
   212  	if nil != err {
   213  		return
   214  	}
   215  
   216  	globals.supportedOnDiskInodeVersions = make(map[Version]struct{})
   217  
   218  	globals.supportedOnDiskInodeVersions[V1] = struct{}{}
   219  
   220  	globals.corruptionDetectedTrueBuf, err = cstruct.Pack(corruptionDetectedTrue, cstruct.LittleEndian)
   221  	if nil != err {
   222  		return
   223  	}
   224  	globals.corruptionDetectedFalseBuf, err = cstruct.Pack(corruptionDetectedFalse, cstruct.LittleEndian)
   225  	if nil != err {
   226  		return
   227  	}
   228  	globals.versionV1Buf, err = cstruct.Pack(versionV1, cstruct.LittleEndian)
   229  	if nil != err {
   230  		return
   231  	}
   232  
   233  	globals.inodeRecDefaultPreambleBuf = make([]byte, 0, len(globals.corruptionDetectedFalseBuf)+len(globals.versionV1Buf))
   234  	globals.inodeRecDefaultPreambleBuf = append(globals.inodeRecDefaultPreambleBuf, globals.corruptionDetectedFalseBuf...)
   235  	globals.inodeRecDefaultPreambleBuf = append(globals.inodeRecDefaultPreambleBuf, globals.versionV1Buf...)
   236  
   237  	swiftclient.SetStarvationCallbackFunc(chunkedPutConnectionPoolStarvationCallback)
   238  
   239  	globals.rwMode = RWModeNormal
   240  
   241  	err = nil
   242  	return
   243  }
   244  
   245  func (dummy *globalsStruct) VolumeGroupCreated(confMap conf.ConfMap, volumeGroupName string, activePeer string, virtualIPAddr string) (err error) {
   246  	var (
   247  		ok                     bool
   248  		volumeGroup            *volumeGroupStruct
   249  		volumeGroupSectionName string
   250  	)
   251  
   252  	volumeGroup = &volumeGroupStruct{
   253  		name:               volumeGroupName,
   254  		volumeMap:          make(map[string]*volumeStruct),
   255  		numServed:          0,
   256  		readCacheLineCount: 0,
   257  		readCache:          make(map[readCacheKeyStruct]*readCacheElementStruct),
   258  		readCacheMRU:       nil,
   259  		readCacheLRU:       nil,
   260  	}
   261  
   262  	volumeGroupSectionName = "VolumeGroup:" + volumeGroupName
   263  
   264  	volumeGroup.virtualIPAddr, err = confMap.FetchOptionValueString(volumeGroupSectionName, "VirtualIPAddr")
   265  	if nil != err {
   266  		if nil == confMap.VerifyOptionValueIsEmpty(volumeGroupSectionName, "VirtualIPAddr") {
   267  			volumeGroup.virtualIPAddr = ""
   268  		} else {
   269  			return
   270  		}
   271  	}
   272  
   273  	if "" == activePeer {
   274  		volumeGroup.activePeerPrivateIPAddr = ""
   275  	} else {
   276  		volumeGroup.activePeerPrivateIPAddr, err = confMap.FetchOptionValueString("Peer:"+activePeer, "PrivateIPAddr")
   277  		if nil != err {
   278  			return
   279  		}
   280  	}
   281  
   282  	volumeGroup.readCacheLineSize, err = confMap.FetchOptionValueUint64(volumeGroupSectionName, "ReadCacheLineSize")
   283  	if nil != err {
   284  		return
   285  	}
   286  
   287  	volumeGroup.readCacheWeight, err = confMap.FetchOptionValueUint64(volumeGroupSectionName, "ReadCacheWeight")
   288  	if nil != err {
   289  		return
   290  	}
   291  	if volumeGroup.readCacheWeight <= 0 {
   292  		logger.Warnf("Section '%s' for VolumeGroup '%s' ReadCacheWeight %d is <= 0; changing to 1",
   293  			volumeGroupSectionName, volumeGroupName, volumeGroup.readCacheWeight)
   294  		volumeGroup.readCacheWeight = 1
   295  	}
   296  
   297  	globals.Lock()
   298  
   299  	_, ok = globals.volumeGroupMap[volumeGroupName]
   300  	if ok {
   301  		globals.Unlock()
   302  		err = fmt.Errorf("inode.VolumeGroupCreated() called for preexisting VolumeGroup (%s)", volumeGroupName)
   303  		return
   304  	}
   305  
   306  	globals.volumeGroupMap[volumeGroupName] = volumeGroup
   307  
   308  	globals.Unlock()
   309  
   310  	err = nil
   311  	return
   312  }
   313  
   314  func (dummy *globalsStruct) VolumeGroupMoved(confMap conf.ConfMap, volumeGroupName string, activePeer string, virtualIPAddr string) (err error) {
   315  	var (
   316  		ok          bool
   317  		volumeGroup *volumeGroupStruct
   318  	)
   319  
   320  	globals.Lock()
   321  
   322  	volumeGroup, ok = globals.volumeGroupMap[volumeGroupName]
   323  	if !ok {
   324  		globals.Unlock()
   325  		err = fmt.Errorf("inode.VolumeGroupMoved() called for nonexistent VolumeGroup (%s)", volumeGroupName)
   326  		return
   327  	}
   328  
   329  	volumeGroup.Lock()
   330  
   331  	if "" == activePeer {
   332  		volumeGroup.activePeerPrivateIPAddr = ""
   333  	} else {
   334  		volumeGroup.activePeerPrivateIPAddr, err = confMap.FetchOptionValueString("Peer:"+activePeer, "PrivateIPAddr")
   335  		if nil != err {
   336  			volumeGroup.Unlock()
   337  			globals.Unlock()
   338  			return
   339  		}
   340  	}
   341  
   342  	// Note that VirtualIPAddr, ReadCacheLineSize, & ReadCacheWeight are not reloaded
   343  
   344  	volumeGroup.Unlock()
   345  	globals.Unlock()
   346  
   347  	err = nil
   348  	return
   349  }
   350  
   351  func (dummy *globalsStruct) VolumeGroupDestroyed(confMap conf.ConfMap, volumeGroupName string) (err error) {
   352  	var (
   353  		ok          bool
   354  		volumeGroup *volumeGroupStruct
   355  	)
   356  
   357  	globals.Lock()
   358  
   359  	volumeGroup, ok = globals.volumeGroupMap[volumeGroupName]
   360  	if !ok {
   361  		globals.Unlock()
   362  		err = fmt.Errorf("inode.VolumeGroupDestroyed() called for nonexistent VolumeGroup (%s)", volumeGroupName)
   363  		return
   364  	}
   365  
   366  	volumeGroup.Lock()
   367  
   368  	if 0 != len(volumeGroup.volumeMap) {
   369  		volumeGroup.Unlock()
   370  		globals.Unlock()
   371  		err = fmt.Errorf("inode.VolumeGroupDestroyed() called for non-empty VolumeGroup (%s)", volumeGroupName)
   372  		return
   373  	}
   374  
   375  	delete(globals.volumeGroupMap, volumeGroupName)
   376  
   377  	volumeGroup.Unlock()
   378  	globals.Unlock()
   379  
   380  	err = nil
   381  	return
   382  }
   383  
   384  func (dummy *globalsStruct) VolumeCreated(confMap conf.ConfMap, volumeName string, volumeGroupName string) (err error) {
   385  	var (
   386  		ok                bool
   387  		volumeGroup       *volumeGroupStruct
   388  		volumeSectionName string
   389  	)
   390  
   391  	volume := &volumeStruct{volumeName: volumeName, served: false}
   392  
   393  	volumeSectionName = "Volume:" + volumeName
   394  
   395  	volume.fsid, err = confMap.FetchOptionValueUint64(volumeSectionName, "FSID")
   396  	if nil != err {
   397  		return
   398  	}
   399  
   400  	volume.accountName, err = confMap.FetchOptionValueString(volumeSectionName, "AccountName")
   401  	if nil != err {
   402  		return
   403  	}
   404  
   405  	globals.Lock()
   406  
   407  	_, ok = globals.volumeMap[volumeName]
   408  	if ok {
   409  		globals.Unlock()
   410  		err = fmt.Errorf("inode.VolumeCreated() called for preexiting Volume (%s)", volumeName)
   411  		return
   412  	}
   413  
   414  	_, ok = globals.accountMap[volume.accountName]
   415  	if ok {
   416  		globals.Unlock()
   417  		err = fmt.Errorf("inode.VolumeCreated() called for preexiting Account (%s)", volume.accountName)
   418  		return
   419  	}
   420  
   421  	volumeGroup, ok = globals.volumeGroupMap[volumeGroupName]
   422  	if !ok {
   423  		globals.Unlock()
   424  		err = fmt.Errorf("inode.VolumeCreated() called for Volume (%s) to be added to nonexistent VolumeGroup (%s)", volumeName, volumeGroupName)
   425  		return
   426  	}
   427  
   428  	volumeGroup.Lock()
   429  
   430  	_, ok = volumeGroup.volumeMap[volumeName]
   431  	if ok {
   432  		volumeGroup.Unlock()
   433  		globals.Unlock()
   434  		err = fmt.Errorf("inode.VolumeCreated() called for preexiting Volume (%s) to be added to VolumeGroup (%s)", volumeName, volumeGroupName)
   435  		return
   436  	}
   437  
   438  	volume.volumeGroup = volumeGroup
   439  	volumeGroup.volumeMap[volumeName] = volume
   440  	globals.volumeMap[volumeName] = volume
   441  	globals.accountMap[volume.accountName] = volume
   442  
   443  	volumeGroup.Unlock()
   444  	globals.Unlock()
   445  
   446  	err = nil
   447  	return
   448  }
   449  
   450  func (dummy *globalsStruct) VolumeMoved(confMap conf.ConfMap, volumeName string, volumeGroupName string) (err error) {
   451  	var (
   452  		newVolumeGroup *volumeGroupStruct
   453  		ok             bool
   454  		oldVolumeGroup *volumeGroupStruct
   455  		volume         *volumeStruct
   456  	)
   457  
   458  	globals.Lock()
   459  
   460  	volume, ok = globals.volumeMap[volumeName]
   461  	if !ok {
   462  		globals.Unlock()
   463  		err = fmt.Errorf("inode.VolumeMoved() called for nonexistent Volume (%s)", volumeName)
   464  		return
   465  	}
   466  
   467  	if volume.served {
   468  		globals.Unlock()
   469  		err = fmt.Errorf("inode.VolumeMoved() called for Volume (%s) being actively served", volumeName)
   470  		return
   471  	}
   472  
   473  	newVolumeGroup, ok = globals.volumeGroupMap[volumeGroupName]
   474  	if !ok {
   475  		globals.Unlock()
   476  		err = fmt.Errorf("inode.VolumeMoved() called for Volume (%s) to be moved to nonexistent VolumeGroup (%s)", volumeName, volumeGroupName)
   477  		return
   478  	}
   479  
   480  	newVolumeGroup.Lock()
   481  
   482  	_, ok = newVolumeGroup.volumeMap[volumeName]
   483  	if ok {
   484  		newVolumeGroup.Unlock()
   485  		globals.Unlock()
   486  		err = fmt.Errorf("inode.VolumeMoved() called for Volume (%s) to be moved to VolumeGroup (%s) already containing the Volume", volumeName, volumeGroupName)
   487  		return
   488  	}
   489  
   490  	oldVolumeGroup = volume.volumeGroup
   491  
   492  	oldVolumeGroup.Lock()
   493  
   494  	delete(oldVolumeGroup.volumeMap, volumeName)
   495  	newVolumeGroup.volumeMap[volumeName] = volume
   496  	volume.volumeGroup = newVolumeGroup
   497  
   498  	// Note that FSID & AccountName are not reloaded
   499  
   500  	oldVolumeGroup.Unlock()
   501  	newVolumeGroup.Unlock()
   502  	globals.Unlock()
   503  
   504  	err = nil
   505  	return
   506  }
   507  
   508  func (dummy *globalsStruct) VolumeDestroyed(confMap conf.ConfMap, volumeName string) (err error) {
   509  	var (
   510  		ok     bool
   511  		volume *volumeStruct
   512  	)
   513  
   514  	globals.Lock()
   515  
   516  	volume, ok = globals.volumeMap[volumeName]
   517  	if !ok {
   518  		globals.Unlock()
   519  		err = fmt.Errorf("inode.VolumeDestroyed() called for nonexistent Volume (%s)", volumeName)
   520  		return
   521  	}
   522  
   523  	if volume.served {
   524  		globals.Unlock()
   525  		err = fmt.Errorf("inode.VolumeDestroyed() called for Volume (%s) being actively served", volumeName)
   526  		return
   527  	}
   528  
   529  	volume.volumeGroup.Lock()
   530  
   531  	delete(volume.volumeGroup.volumeMap, volumeName)
   532  	delete(globals.volumeMap, volumeName)
   533  	delete(globals.accountMap, volume.accountName)
   534  
   535  	volume.volumeGroup.Unlock()
   536  	globals.Unlock()
   537  
   538  	err = nil
   539  	return
   540  }
   541  
   542  func (dummy *globalsStruct) ServeVolume(confMap conf.ConfMap, volumeName string) (err error) {
   543  	var (
   544  		defaultPhysicalContainerLayout            *physicalContainerLayoutStruct
   545  		defaultPhysicalContainerLayoutName        string
   546  		defaultPhysicalContainerLayoutSectionName string
   547  		ok                                        bool
   548  		volume                                    *volumeStruct
   549  		volumeSectionName                         string
   550  	)
   551  
   552  	volumeSectionName = "Volume:" + volumeName
   553  
   554  	globals.Lock()
   555  
   556  	volume, ok = globals.volumeMap[volumeName]
   557  	if !ok {
   558  		globals.Unlock()
   559  		err = fmt.Errorf("inode.ServeVolume() called for nonexistent Volume (%s)", volumeName)
   560  		return
   561  	}
   562  	if volume.served {
   563  		globals.Unlock()
   564  		err = fmt.Errorf("inode.ServeVolume() called for Volume (%s) already being served", volumeName)
   565  		return
   566  	}
   567  
   568  	volume.maxEntriesPerDirNode, err = confMap.FetchOptionValueUint64(volumeSectionName, "MaxEntriesPerDirNode")
   569  	if nil != err {
   570  		globals.Unlock()
   571  		return
   572  	}
   573  	volume.maxExtentsPerFileNode, err = confMap.FetchOptionValueUint64(volumeSectionName, "MaxExtentsPerFileNode")
   574  	if nil != err {
   575  		globals.Unlock()
   576  		return
   577  	}
   578  	defaultPhysicalContainerLayoutName, err = confMap.FetchOptionValueString(volumeSectionName, "DefaultPhysicalContainerLayout")
   579  	if nil != err {
   580  		globals.Unlock()
   581  		return
   582  	}
   583  
   584  	defaultPhysicalContainerLayout = &physicalContainerLayoutStruct{
   585  		name:                        defaultPhysicalContainerLayoutName,
   586  		containerNameSliceNextIndex: 0,
   587  		containerNameSliceLoopCount: 0,
   588  	}
   589  
   590  	defaultPhysicalContainerLayoutSectionName = "PhysicalContainerLayout:" + defaultPhysicalContainerLayoutName
   591  
   592  	defaultPhysicalContainerLayout.containerStoragePolicy, err = confMap.FetchOptionValueString(defaultPhysicalContainerLayoutSectionName, "ContainerStoragePolicy")
   593  	if nil != err {
   594  		globals.Unlock()
   595  		return
   596  	}
   597  	defaultPhysicalContainerLayout.containerNamePrefix, err = confMap.FetchOptionValueString(defaultPhysicalContainerLayoutSectionName, "ContainerNamePrefix")
   598  	if nil != err {
   599  		globals.Unlock()
   600  		return
   601  	}
   602  	defaultPhysicalContainerLayout.containersPerPeer, err = confMap.FetchOptionValueUint64(defaultPhysicalContainerLayoutSectionName, "ContainersPerPeer")
   603  	if nil != err {
   604  		globals.Unlock()
   605  		return
   606  	}
   607  	defaultPhysicalContainerLayout.maxObjectsPerContainer, err = confMap.FetchOptionValueUint64(defaultPhysicalContainerLayoutSectionName, "MaxObjectsPerContainer")
   608  	if nil != err {
   609  		globals.Unlock()
   610  		return
   611  	}
   612  
   613  	defaultPhysicalContainerLayout.containerNameSlice = make([]string, defaultPhysicalContainerLayout.containersPerPeer)
   614  
   615  	volume.defaultPhysicalContainerLayout = defaultPhysicalContainerLayout
   616  
   617  	volume.maxFlushSize, err = confMap.FetchOptionValueUint64(volumeSectionName, "MaxFlushSize")
   618  	if nil != err {
   619  		globals.Unlock()
   620  		return
   621  	}
   622  
   623  	volume.headhunterVolumeHandle, err = headhunter.FetchVolumeHandle(volume.volumeName)
   624  	if nil != err {
   625  		globals.Unlock()
   626  		return
   627  	}
   628  
   629  	volume.headhunterVolumeHandle.RegisterForEvents(volume)
   630  
   631  	volume.inodeCache = sortedmap.NewLLRBTree(compareInodeNumber, volume)
   632  	volume.inodeCacheStopChan = make(chan struct{}, 0)
   633  	volume.inodeCacheLRUHead = nil
   634  	volume.inodeCacheLRUTail = nil
   635  	volume.inodeCacheLRUItems = 0
   636  
   637  	err = startInodeCacheDiscard(confMap, volume, volumeSectionName)
   638  	if nil != err {
   639  		globals.Unlock()
   640  		return
   641  	}
   642  
   643  	volume.volumeGroup.Lock()
   644  
   645  	volume.served = true
   646  	volume.volumeGroup.numServed++
   647  
   648  	volume.volumeGroup.Unlock()
   649  
   650  	globals.Unlock()
   651  
   652  	err = adoptVolumeGroupReadCacheParameters(confMap)
   653  
   654  	return // err from call to adoptVolumeGroupReadCacheParameters() is fine to return here
   655  }
   656  
   657  func (dummy *globalsStruct) UnserveVolume(confMap conf.ConfMap, volumeName string) (err error) {
   658  	var (
   659  		ok     bool
   660  		volume *volumeStruct
   661  	)
   662  
   663  	globals.Lock()
   664  
   665  	volume, ok = globals.volumeMap[volumeName]
   666  	if !ok {
   667  		globals.Unlock()
   668  		err = fmt.Errorf("inode.UnserveVolume() called for nonexistent Volume (%s)", volumeName)
   669  		return
   670  	}
   671  	if !volume.served {
   672  		globals.Unlock()
   673  		err = fmt.Errorf("inode.UnserveVolume() called for Volume (%s) not being served", volumeName)
   674  		return
   675  	}
   676  
   677  	stopInodeCacheDiscard(volume)
   678  
   679  	volume.inodeCache = nil
   680  	volume.inodeCacheStopChan = nil
   681  	volume.inodeCacheLRUHead = nil
   682  	volume.inodeCacheLRUTail = nil
   683  	volume.inodeCacheLRUItems = 0
   684  
   685  	volume.headhunterVolumeHandle.UnregisterForEvents(volume)
   686  
   687  	volume.volumeGroup.Lock()
   688  
   689  	volume.served = false
   690  	volume.volumeGroup.numServed--
   691  
   692  	volume.volumeGroup.Unlock()
   693  	globals.Unlock()
   694  
   695  	err = adoptVolumeGroupReadCacheParameters(confMap)
   696  
   697  	return // err from call to adoptVolumeGroupReadCacheParameters() is fine to return here
   698  }
   699  
   700  func (dummy *globalsStruct) VolumeToBeUnserved(confMap conf.ConfMap, volumeName string) (err error) {
   701  	err = nil
   702  	return
   703  }
   704  
   705  func (dummy *globalsStruct) SignaledStart(confMap conf.ConfMap) (err error) {
   706  	var (
   707  		volume *volumeStruct
   708  	)
   709  
   710  	for _, volume = range globals.volumeMap {
   711  		if volume.served && (nil != volume.snapShotPolicy) {
   712  			volume.snapShotPolicy.down()
   713  			volume.snapShotPolicy = nil
   714  		}
   715  	}
   716  
   717  	err = nil
   718  	return
   719  }
   720  
   721  func (dummy *globalsStruct) SignaledFinish(confMap conf.ConfMap) (err error) {
   722  	var (
   723  		swiftReconNoWriteErrno  string
   724  		swiftReconReadOnlyErrno string
   725  		volume                  *volumeStruct
   726  	)
   727  
   728  	swiftReconNoWriteErrno, err = confMap.FetchOptionValueString("SwiftClient", "SwiftReconNoWriteErrno")
   729  	if nil == err {
   730  		switch swiftReconNoWriteErrno {
   731  		case "EPERM":
   732  			globals.noWriteThresholdErrno = blunder.NotPermError
   733  			globals.noWriteThresholdErrnoString = "EPERM"
   734  		case "EROFS":
   735  			globals.noWriteThresholdErrno = blunder.ReadOnlyError
   736  			globals.noWriteThresholdErrnoString = "EROFS"
   737  		case "ENOSPC":
   738  			globals.noWriteThresholdErrno = blunder.NoSpaceError
   739  			globals.noWriteThresholdErrnoString = "ENOSPC"
   740  		default:
   741  			err = fmt.Errorf("[SwiftClient]SwiftReconReadOnlyErrno must be either EPERM or EROFS or ENOSPC")
   742  			return
   743  		}
   744  	} else {
   745  		logger.WarnfWithError(err, "Unable to fetch [SwiftClient]SwiftReconNoWriteErrno... defaulting to %s", defaultNoWriteErrno)
   746  		globals.noWriteThresholdErrno = defaultNoWriteErrno
   747  		globals.noWriteThresholdErrnoString = defaultNoWriteErrnoString
   748  	}
   749  
   750  	swiftReconReadOnlyErrno, err = confMap.FetchOptionValueString("SwiftClient", "SwiftReconReadOnlyErrno")
   751  	if nil == err {
   752  		switch swiftReconReadOnlyErrno {
   753  		case "EPERM":
   754  			globals.readOnlyThresholdErrno = blunder.NotPermError
   755  			globals.readOnlyThresholdErrnoString = "EPERM"
   756  		case "EROFS":
   757  			globals.readOnlyThresholdErrno = blunder.ReadOnlyError
   758  			globals.readOnlyThresholdErrnoString = "EROFS"
   759  		case "ENOSPC":
   760  			globals.readOnlyThresholdErrno = blunder.NoSpaceError
   761  			globals.readOnlyThresholdErrnoString = "ENOSPC"
   762  		default:
   763  			err = fmt.Errorf("[SwiftClient]SwiftReconReadOnlyErrno must be either EPERM or EROFS or ENOSPC")
   764  			return
   765  		}
   766  	} else {
   767  		logger.WarnfWithError(err, "Unable to fetch [SwiftClient]SwiftReconReadOnlyErrno... defaulting to %s", defaultReadOnlyErrno)
   768  		globals.readOnlyThresholdErrno = defaultReadOnlyErrno
   769  		globals.readOnlyThresholdErrnoString = defaultReadOnlyErrnoString
   770  	}
   771  
   772  	for _, volume = range globals.volumeMap {
   773  		if volume.served {
   774  			err = volume.loadSnapShotPolicy(confMap)
   775  			if nil != err {
   776  				return
   777  			}
   778  			if nil != volume.snapShotPolicy {
   779  				volume.snapShotPolicy.up()
   780  			}
   781  		}
   782  	}
   783  
   784  	err = nil
   785  	return
   786  }
   787  
   788  func (dummy *globalsStruct) Down(confMap conf.ConfMap) (err error) {
   789  	if 0 != len(globals.volumeGroupMap) {
   790  		err = fmt.Errorf("inode.Down() called with 0 != len(globals.volumeGroupMap")
   791  		return
   792  	}
   793  	if 0 != len(globals.volumeMap) {
   794  		err = fmt.Errorf("inode.Down() called with 0 != len(globals.volumeMap")
   795  		return
   796  	}
   797  
   798  	err = nil
   799  	return
   800  }