github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/inode/config.go (about)

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