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 }