nbdman.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package nbd
  15. import (
  16. "fmt"
  17. "sync"
  18. "time"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/onecloud/pkg/util/fileutils2"
  22. "yunion.io/x/onecloud/pkg/util/procutils"
  23. "yunion.io/x/onecloud/pkg/util/sysutils"
  24. )
  25. type SNBDManager struct {
  26. nbdDevs map[string]bool
  27. nbdChan chan struct{}
  28. nbdLock *sync.Mutex
  29. }
  30. var nbdManager *SNBDManager
  31. func Init() error {
  32. var err error
  33. nbdManager, err = NewNBDManager()
  34. return err
  35. }
  36. func GetNBDManager() *SNBDManager {
  37. return nbdManager
  38. }
  39. func NewNBDManager() (*SNBDManager, error) {
  40. var ret = new(SNBDManager)
  41. ret.nbdDevs = make(map[string]bool, 0)
  42. ret.nbdLock = new(sync.Mutex)
  43. ret.cleanupNbdDevices()
  44. if err := ret.reloadNbdDevices(); err != nil {
  45. return ret, errors.Wrap(err, "reloadNbdDevices")
  46. }
  47. if err := ret.findNbdDevices(); err != nil {
  48. return ret, errors.Wrap(err, "findNbdDevices")
  49. }
  50. ret.nbdChan = make(chan struct{}, len(ret.nbdDevs))
  51. return ret, nil
  52. }
  53. func tryDetachNbd(nbddev string) error {
  54. const MaxTries = 3
  55. tried := 0
  56. var errs []error
  57. for tried < MaxTries && fileutils2.IsBlockDeviceUsed(nbddev) {
  58. tried++
  59. if err := putdownNbdDevice(nbddev); err != nil {
  60. errs = append(errs, err)
  61. time.Sleep(time.Second)
  62. continue
  63. }
  64. break
  65. }
  66. if tried < MaxTries {
  67. return nil
  68. }
  69. if len(errs) > 0 {
  70. return errors.NewAggregate(errs)
  71. }
  72. return nil
  73. }
  74. func putdownNbdDevice(nbddev string) error {
  75. nbdDriver := &NBDDriver{nbdDev: nbddev}
  76. if err := nbdDriver.findPartitions(); err != nil {
  77. return err
  78. }
  79. if _, err := nbdDriver.setupLVMS(); err != nil {
  80. return err
  81. }
  82. for i := range nbdDriver.partitions {
  83. if nbdDriver.partitions[i].IsMounted() {
  84. if err := nbdDriver.partitions[i].Umount(); err != nil {
  85. return errors.Wrapf(err, "umount %s", nbdDriver.partitions[i].GetPartDev())
  86. }
  87. }
  88. }
  89. if !nbdDriver.putdownLVMs() {
  90. return errors.Errorf("failed putdown lvms")
  91. }
  92. if err := QemuNbdDisconnect(nbddev); err != nil {
  93. return err
  94. }
  95. return nil
  96. }
  97. func (m *SNBDManager) cleanupNbdDevices() {
  98. var i = 0
  99. for {
  100. nbddev := fmt.Sprintf("/dev/nbd%d", i)
  101. if fileutils2.Exists(nbddev) {
  102. if fileutils2.IsBlockDeviceUsed(nbddev) {
  103. log.Infof("nbd device %s is used", nbddev)
  104. err := tryDetachNbd(nbddev)
  105. if err != nil {
  106. log.Errorf("tryDetachNbd fail %s", err)
  107. } else {
  108. m.ReleaseNbddev(nbddev)
  109. }
  110. }
  111. i++
  112. } else {
  113. break
  114. }
  115. }
  116. }
  117. func (m *SNBDManager) reloadNbdDevices() error {
  118. output, err := procutils.NewRemoteCommandAsFarAsPossible("rmmod", "nbd").Output()
  119. if err != nil {
  120. log.Errorf("rmmod error: %s", output)
  121. }
  122. output, err = procutils.NewRemoteCommandAsFarAsPossible("modprobe", "nbd", "max_part=16").Output()
  123. if err != nil {
  124. return errors.Wrapf(err, "Failed to activate nbd device: %s", output)
  125. }
  126. return nil
  127. }
  128. func (m *SNBDManager) findNbdDevices() error {
  129. var i = 0
  130. for {
  131. nbddev := fmt.Sprintf("/dev/nbd%d", i)
  132. if fileutils2.Exists(nbddev) {
  133. i++
  134. if fileutils2.IsBlockDeviceUsed(nbddev) {
  135. continue
  136. }
  137. m.nbdDevs[nbddev] = false
  138. // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-bdi
  139. nbdBdi := fmt.Sprintf("/sys/block/nbd%d/bdi/", i-1)
  140. sysutils.SetSysConfig(nbdBdi+"max_ratio", "0")
  141. sysutils.SetSysConfig(nbdBdi+"min_ratio", "0")
  142. } else {
  143. break
  144. }
  145. }
  146. if len(m.nbdDevs) == 0 {
  147. return errors.Wrap(errors.ErrNotFound, "No nbd devices found")
  148. }
  149. log.Infof("NBD_DEVS: %#v", m.nbdDevs)
  150. return nil
  151. }
  152. func (m *SNBDManager) AcquireNbddev() string {
  153. if len(m.nbdDevs) == 0 {
  154. return ""
  155. }
  156. m.nbdChan <- struct{}{}
  157. defer m.nbdLock.Unlock()
  158. m.nbdLock.Lock()
  159. for nbdDev := range m.nbdDevs {
  160. if fileutils2.IsBlockDeviceUsed(nbdDev) {
  161. m.nbdDevs[nbdDev] = true
  162. continue
  163. }
  164. if !m.nbdDevs[nbdDev] {
  165. m.nbdDevs[nbdDev] = true
  166. return nbdDev
  167. }
  168. }
  169. return ""
  170. }
  171. func (m *SNBDManager) ReleaseNbddev(nbddev string) {
  172. if _, ok := m.nbdDevs[nbddev]; ok {
  173. defer m.nbdLock.Unlock()
  174. m.nbdLock.Lock()
  175. if !fileutils2.IsBlockDeviceUsed(nbddev) {
  176. m.nbdDevs[nbddev] = false
  177. }
  178. }
  179. <-m.nbdChan
  180. }