gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/device/drivers/block.go (about)

     1  // Copyright (c) 2017-2018 Intel Corporation
     2  // Copyright (c) 2018-2019 Huawei Corporation
     3  //
     4  // SPDX-License-Identifier: Apache-2.0
     5  //
     6  
     7  package drivers
     8  
     9  import (
    10  	"path/filepath"
    11  
    12  	"github.com/kata-containers/runtime/virtcontainers/device/api"
    13  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    14  	persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
    15  	"github.com/kata-containers/runtime/virtcontainers/utils"
    16  )
    17  
    18  const maxDevIDSize = 31
    19  
    20  // BlockDevice refers to a block storage device implementation.
    21  type BlockDevice struct {
    22  	*GenericDevice
    23  	BlockDrive *config.BlockDrive
    24  }
    25  
    26  // NewBlockDevice creates a new block device based on DeviceInfo
    27  func NewBlockDevice(devInfo *config.DeviceInfo) *BlockDevice {
    28  	return &BlockDevice{
    29  		GenericDevice: &GenericDevice{
    30  			ID:         devInfo.ID,
    31  			DeviceInfo: devInfo,
    32  		},
    33  	}
    34  }
    35  
    36  // Attach is standard interface of api.Device, it's used to add device to some
    37  // DeviceReceiver
    38  func (device *BlockDevice) Attach(devReceiver api.DeviceReceiver) (err error) {
    39  	skip, err := device.bumpAttachCount(true)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	if skip {
    44  		return nil
    45  	}
    46  
    47  	// Increment the block index for the sandbox. This is used to determine the name
    48  	// for the block device in the case where the block device is used as container
    49  	// rootfs and the predicted block device name needs to be provided to the agent.
    50  	index, err := devReceiver.GetAndSetSandboxBlockIndex()
    51  
    52  	defer func() {
    53  		if err != nil {
    54  			devReceiver.UnsetSandboxBlockIndex(index)
    55  			device.bumpAttachCount(false)
    56  		}
    57  	}()
    58  
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	drive := &config.BlockDrive{
    64  		File:   device.DeviceInfo.HostPath,
    65  		Format: "raw",
    66  		ID:     utils.MakeNameID("drive", device.DeviceInfo.ID, maxDevIDSize),
    67  		Index:  index,
    68  		Pmem:   device.DeviceInfo.Pmem,
    69  	}
    70  
    71  	if fs, ok := device.DeviceInfo.DriverOptions["fstype"]; ok {
    72  		drive.Format = fs
    73  	}
    74  
    75  	customOptions := device.DeviceInfo.DriverOptions
    76  	if customOptions == nil ||
    77  		customOptions["block-driver"] == "virtio-scsi" {
    78  		// User has not chosen a specific block device type
    79  		// Default to SCSI
    80  		scsiAddr, err := utils.GetSCSIAddress(index)
    81  		if err != nil {
    82  			return err
    83  		}
    84  
    85  		drive.SCSIAddr = scsiAddr
    86  	} else if customOptions["block-driver"] != "nvdimm" {
    87  		var globalIdx int
    88  
    89  		switch customOptions["block-driver"] {
    90  		case "virtio-blk":
    91  			globalIdx = index
    92  		case "virtio-blk-ccw":
    93  			globalIdx = index
    94  		case "virtio-mmio":
    95  			//With firecracker the rootfs for the VM itself
    96  			//sits at /dev/vda and consumes the first index.
    97  			//Longer term block based VM rootfs should be added
    98  			//as a regular block device which eliminates the
    99  			//offset.
   100  			//https://github.com/kata-containers/runtime/issues/1061
   101  			globalIdx = index + 1
   102  		}
   103  
   104  		driveName, err := utils.GetVirtDriveName(globalIdx)
   105  		if err != nil {
   106  			return err
   107  		}
   108  
   109  		drive.VirtPath = filepath.Join("/dev", driveName)
   110  	}
   111  
   112  	deviceLogger().WithField("device", device.DeviceInfo.HostPath).WithField("VirtPath", drive.VirtPath).Infof("Attaching %s device", customOptions["block-driver"])
   113  	device.BlockDrive = drive
   114  	if err = devReceiver.HotplugAddDevice(device, config.DeviceBlock); err != nil {
   115  		return err
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  // Detach is standard interface of api.Device, it's used to remove device from some
   122  // DeviceReceiver
   123  func (device *BlockDevice) Detach(devReceiver api.DeviceReceiver) error {
   124  	skip, err := device.bumpAttachCount(false)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	if skip {
   129  		return nil
   130  	}
   131  
   132  	defer func() {
   133  		if err != nil {
   134  			device.bumpAttachCount(true)
   135  		} else {
   136  			devReceiver.UnsetSandboxBlockIndex(device.BlockDrive.Index)
   137  		}
   138  	}()
   139  
   140  	deviceLogger().WithField("device", device.DeviceInfo.HostPath).Info("Unplugging block device")
   141  
   142  	if err = devReceiver.HotplugRemoveDevice(device, config.DeviceBlock); err != nil {
   143  		deviceLogger().WithError(err).Error("Failed to unplug block device")
   144  		return err
   145  	}
   146  	return nil
   147  }
   148  
   149  // DeviceType is standard interface of api.Device, it returns device type
   150  func (device *BlockDevice) DeviceType() config.DeviceType {
   151  	return config.DeviceBlock
   152  }
   153  
   154  // GetDeviceInfo returns device information used for creating
   155  func (device *BlockDevice) GetDeviceInfo() interface{} {
   156  	return device.BlockDrive
   157  }
   158  
   159  // Save converts Device to DeviceState
   160  func (device *BlockDevice) Save() persistapi.DeviceState {
   161  	ds := device.GenericDevice.Save()
   162  	ds.Type = string(device.DeviceType())
   163  
   164  	drive := device.BlockDrive
   165  	if drive != nil {
   166  		ds.BlockDrive = &persistapi.BlockDrive{
   167  			File:     drive.File,
   168  			Format:   drive.Format,
   169  			ID:       drive.ID,
   170  			Index:    drive.Index,
   171  			MmioAddr: drive.MmioAddr,
   172  			PCIAddr:  drive.PCIAddr,
   173  			SCSIAddr: drive.SCSIAddr,
   174  			NvdimmID: drive.NvdimmID,
   175  			VirtPath: drive.VirtPath,
   176  			DevNo:    drive.DevNo,
   177  			Pmem:     drive.Pmem,
   178  		}
   179  	}
   180  	return ds
   181  }
   182  
   183  // Load loads DeviceState and converts it to specific device
   184  func (device *BlockDevice) Load(ds persistapi.DeviceState) {
   185  	device.GenericDevice = &GenericDevice{}
   186  	device.GenericDevice.Load(ds)
   187  
   188  	bd := ds.BlockDrive
   189  	if bd == nil {
   190  		return
   191  	}
   192  	device.BlockDrive = &config.BlockDrive{
   193  		File:     bd.File,
   194  		Format:   bd.Format,
   195  		ID:       bd.ID,
   196  		Index:    bd.Index,
   197  		MmioAddr: bd.MmioAddr,
   198  		PCIAddr:  bd.PCIAddr,
   199  		SCSIAddr: bd.SCSIAddr,
   200  		NvdimmID: bd.NvdimmID,
   201  		VirtPath: bd.VirtPath,
   202  		DevNo:    bd.DevNo,
   203  		Pmem:     bd.Pmem,
   204  	}
   205  }
   206  
   207  // It should implement GetAttachCount() and DeviceID() as api.Device implementation
   208  // here it shares function from *GenericDevice so we don't need duplicate codes